系统配置
部署dmdbms时,需要设置好相关的操作系统参数,例如用户资源限制,DSC架构下的共享内存等。
CPU
CPU决定了计算速度,一组基本参数控制了 CBO 的代价计算行为,并影响着最终的结果。这些参数可以在dm.ini 中设置,但是通常不要轻易修改。任何对这些参数的修改,都可能对优化器的结果产生重大影响。当前优化器的参数值在 V
D
M
I
N
I
在
定
义
。
关
于
V
DM_INI 在定义。关于 V
DMINI在定义。关于VDM_INI, 这是一个与 dm.ini对应的 V
视
图
,
它
允
许
用
户
可
以
不
打
开
d
m
.
i
n
i
配
置
文
件
,
就
可
以
查
询
每
个
参
数
的
当
前
值
。
对
于
没
有
在
d
m
.
i
n
i
中
显
式
设
置
的
参
数
,
V
视图,它允许用户可以不打开 dm.ini 配置文件,就可以查询每个参数的当前值。对于没有在 dm.ini 中显式设置的参数, V
视图,它允许用户可以不打开dm.ini配置文件,就可以查询每个参数的当前值。对于没有在dm.ini中显式设置的参数,VDM_INI 中显示的是系统的缺省值。
为了表示 CPU 代价, CBO 假定一些“标准”的数据库操作占用了一定数量的 CPU 时钟周期,因此 CPU 的工作频率决定了执行“标准”操作的时间。 CPU 的缺省速度工作在 3Ghz, 用参数 CPU_SPEED来表示。 这个参数的意义是每 1ms CPU的时钟周期数目, 注意是毫秒, 可以查询 V$DM_INI ,来获得这个值:
select * from v$dm_ini where para_name = 'CPU_SPEED'
系统执行任何一个数据库操作,如扫描,过滤,无论处理的记录数有多少,都有一个初始化过程,比如申请一段内存,做必要的封锁等等,即便仅仅扫描一条记录也需要这些工作,因此需要一个门槛值,作为基础代价。
select * from v$dm_ini where para_name like '%CPU';
简单的介绍一下各个参数的含义:
BASE决定了基础代价,折合时间的话,以BASE_SCAN_CPU为例,大约需要0.0445ms,也就是说在CBO计算代价时,做一次扫描(SCAN)准备工作至少需要140000个时钟周期,而实际SCAN_CPU表示做SCAN操作符处理一条简单记录的平均时间是330个时钟周期,而SEEK则是400个。
SCAN: 全表或全索引扫描
SEEK: 索引定位范围扫描,
LKUP: 2 级索引在主索引中查找字段, 配合 SEEK 使用
NLIJ: NEST LOOP INDEX JOIN
HI: HASH 连接, 计算代价时,用于所有的 HASH, 包括 HASH 半连接
HI_SEARCH: 这是在 hash 表探测的代价
MI: 归并连接
NL: NEST LOOP JOIN
FLT: 过滤, FLT_CPU 也用于任何表达式计算的代价评估
示例:
create table t1 as select level id , 'test'||level name from dual connect by level <100001;
explain select count(*) from t1 where id != 1;
1 #NSET2: [11, 1, 4]
2 #PRJT2: [11, 1, 4]; exp_num(1), is_atom(FALSE)
3 #AAGR2: [11, 1, 4]; grp_num(0), sfun_num(1) slave_empty(0)
4 #SLCT2: [11, 5000, 4]; T1.ID <> 1
5 #CSCN2: [11, 100000, 4]; INDEX33555827(T1)
每个计划的操作符都是一个三元组
第一个数字代表的是该操作需要的代价
第二个数字代表估算该操作输出的行数
第三个数字表示每行记录的字节数
按照这个估算计划,执行该操作大约需要耗时11ms,count()输出一行记录。那么计算这个查询估算的时间T1:
T1=(BASE_SCAN_CPU+NSCAN_CPU)/CPU_SPEED=10.535ms
计算SLCT2操作符的耗时T2:
T2=(BASE_FLT_CPU+N*FLT_CPU)/CPU_SPEED=0.998ms
总的查询时间为T=T1+T2=11.533ms,取整为11ms。
实际在我的机器上执行这条语句只需要2ms。涉及到CPU的计算速度及L2缓存,这也是为什么我们需要在计算服务器中用最好的CPU的原因。
内存
DM使用的内存可以分为三部分,缓冲区、内存池、其他内存区。
缓冲区
数据缓冲区:
从磁盘中读取的数据页在内存中的镜像,dm.ini中的BUFFER、FAST_POOL、RECYCLE、KEEP等,普通数据页使用LRU算法淘汰。每条SQL语句请求的数据,都是从数据缓冲区中取得的,若不存在,才会从磁盘中读取数据并加载到缓冲区中。
日志缓冲区:
日志缓冲区对应ini参数中的RLOG_BUF_SIZE,数据库日志将对磁盘的随机写转换成顺序写。
字典缓冲区:
字典缓冲区是保存数据库对象的一片缓冲区,对应INI参数DICT_CACHE_SIZE, DM里面数据对象其实对应的是系统表上的一些信息,内存中的数据对象是通过将系统表上的信息取出并解析出来得到的,该缓冲区一是避免了频繁向磁盘请求获取系统表信息,二是可以减少系统表信息解析开销,在数据对象较多(比如存在非常多分区很多的表)时建议放大。
LRU算法进行淘汰,可以通过视图v$db_cache查看。
SQL缓冲区
SQL CACHE POOL,简称SCP,对应INI参数CACHE_POOL_SIZE,是用来存储包信息(PACKAGE)、执行计划、结果集缓存的一片专用缓存区域,对于SQL类别比较多,或者PKG比较多、复杂的系统,建议将该参数调大。
内存池
服务器启动时从操作系统申请的一大片内存,后续服务器运行过程中,一般情况下,很多需要内存分配的地方都是从该池分配,如果需要的内存大于配置值(MEM_POOL),该池也会自动扩展,一般情况下不收缩,最大扩展到MAX_OS_MEMORY大小。
其他运行内存池
服务器运行过程中,内存的使用有两种模式,一种是直接从内存池申请需要的内存大小,另外一种方式是从操作系统申请一大片内存来做成自己模块的内存池来使用(VM_POOL、SESS_POOL 、 RT_HEAP等等),这样可以减少频繁从数据库主内存池申请内存的开销,一般来说一个会话可以理解为一个单独的运行环境,有自己的私有内存池(HASH SORT 等操作都时从自己的私有池上申请内存),部分少量的从主池申请,另外还有部分线程或者子系统也拥有自己独立的内存池,如CKPT刷盘线程有CKPT_POOL,序列管理有NSEQ_POOL 内存池。
所以在实际硬件配置中,尽量使用大内存来存放所有的数据页,以减少从磁盘中读取数据的开销,内存的速度比磁盘快的多的多。
内存与SQL执行
引申问题-如何确诊内存泄露
select id,classid,name,stat_val from v$sysstat where classid = 11;
通过TOP命令,查看数据库进程的res和virt值
若二者相差不大,那么内存无泄露,若差距较大,一般可以确诊为内存泄露。
OOM相关问题
Linux触发OOM的机制
Linux内核有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽内核会把该进程杀掉。
内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory()被触发,然后调用 select_bad_process() 选择一个“bad”进程杀掉,判断和选择一个“bad”进程的过程由 oom_badness()决定,最 bad 的那个进程就是那个最占用内存的进程。
oom_badness() 给每个进程打分,根据 points 的高低来决定杀哪个进程,这个 points 可以根据 adj 调节,root 权限的进程通常被认为很重要,不应该被轻易杀掉,所以打分的时候可以得到 3% 的优惠(分数越低越不容易被杀掉)。我们可以在用户空间通过操作每个进程的 oom_adj 内核参数来决定哪些进程不这么容易被 OOM killer 选中杀掉。
OOM的预防措施
调整进程的评分等级
echo -17 > /proc/$PID/oom_adj
这个操作,可以强制指定进程号为$pid的进程不被oom杀死
合理配置内存相关参数,MAX_OS_MEMORY和MEMORY_TARGET
ARENA内存分配机制
通常操作系统的默认值是4,这样每个线程都会自己预先分配一部分内存,而每个会话连入数据库时,都会创建一个对应的线程,在连接数较多时,会占用大量的内存,这样MAX_OS_MEMORY可能会限制不住实际dmserver进程的内存使用导致最后触发OOM,修改方式,在dmdba用户的环境变量要添加MALLOC_ARENA_MAX=1,这样可以显著的减少高会话连接时系统virt的内存使用值,减少触发OOM的风险。
磁盘
所有数据最终还是要存储到磁盘中,因此磁盘的读写速度决定了入库的性能。服务器IO的最小操作单元式块,block size,
测试磁盘读写速度
$ dd if=/dev/zero of=/dbdata/dmdata/test bs=32k count=20k oflag=dsync
磁盘调度
不同类型的存储,使用不同的调度算法。
SSD固态硬盘:NOOP调度
SAS机械盘:DEADLINE调度
Raid阵列:RAID0(条带化,空间和性能优势,容错性差), RAID10(速度快,差错控制好)
Linux系统一般默认使用CFQ调度,修改磁盘调度方法:
查看调度:cat /sys/block/sda/queue/scheduler
临时修改:echo “deadline” > /sys/block/sda/queue/scheduler
永久修改: grubby --update-kernel=ALL --args=“elevator=deadline”
RAID卡写模式
在bios中检查raid卡的写模式,必须配置为write back/write back with BBU。
操作系统
在确认以上基础硬件后需要对操作系统的内核参数检查,以Linux为例,下面列举几个比较重要的参数。
共享内存
cat /proc/sys/kernel/shmmax
cat /proc/sys/kernel/shmall
cat /proc/sys/kernel/shmmax