2013,新春巨献,如何提高DBWR的写性能,减少db file parallel write等待
2013,新春巨献,如何提高DBWR的写性能。
其实称不上巨了,做一会标题党。一直在写书,没时间系统的为大家整理Oracle的。前段时间在研究DBWR,2013来了,正好为大家分享一个研究结果。
根据我的测试,DBWR在写脏块的时候,先将脏块分成Batch,再在Batch中合并脏块减少IO次数,然后写脏块。一个Batch内的多次IO是异步的(如果异步IO可用的情况下),但要等一个Batch内所有异步IO都完成,才会开始下一个Batch。决定DBWR写性能的因素,除了存储的好坏外,还有就是Batch的大小、IO的次数。
我们平常在AWR中看到的Physical writes,是写IO的块数。而写IO的次数,需要到v$filestat中查看,对应AWR报告中的IO Stat部分。
还有一个重要的指标,Batch数,如何能查到Batch数呢?其实很简单,db file parallel write等待事件的次数,就是Batch数。
写相同数量的块,次数越少(每次写的越多)、Batch数越少(每个Batch越大),性能就越好。
写IO次数,我们很难控制,因为只有相邻的块才可以合并,我们无法控制用户要修改哪些块。但,Batch的大小,我们是可以控制的。
从10G之后,Oracle多了一个隐藏参数:_db_writer_coalesce_area_size。它就是控制Batch大小的。在我的11GR2测试环境,它的默认值是816K,我分别测试了一下,816K、8160K下,db file parallel write等待事件的变化:
以A2_70M为测试表,它的大小为72M:
SQL> select bytes/1024/1024 sz from user_segments where segment_name='A2_70M';
SZ
----------
72
分别在_db_writer_coalesce_area_size为816K和8160K时,Update这张测试表,查看db file parallel write等待事件,下面是结果,我分别做了三次
次数 时间 次数 时间
------ ------ ----- ---------
次 840 2030 196 1047
第二次 1156 5957 237 2075
第三次 1104 6451 226 1590
前两列是_db_writer_coalesce_area_size为816K时的结果。后两列是_db_writer_coalesce_area_size为8160K的结果。
当_db_writer_coalesce_area_size增大后,db file parallel write等待次数、等待时间都大大减少。
大家可以测试一下,我的测试环境是11GR2,我分别在Linux、AIX、Solaris下测试,效果都非常明显。的遗憾,这是个隐藏参数,它的影响,还需要进一步确认。
这个参数是如何发现的,我没有内部资料,也没有Oracle的内部帐号。有些Puber问我要内部资料,我真的没有这个。如果谁有什么好的、看不懂的资料,欢迎发给我大家一起研究。我的全部帖子,都是靠DTrace和gdb/mdb发现的。除了ASM文件结构,这是参考一篇网上下的资料,还有RAC的锁分析,参考DSI 408。其实,有了Dtrace和gdb/mdb,内部资料意义已经不大了。
发现过程,我不再总结,的无聊透顶。
如果有兴趣一起研究,我将几个函数说一下。
kcbhpbwt是要写哪个脏Buffer时,Oracle会调用的函数,它的个参数,是Buffer地址,也就是x$bh.ba列。通过它,可以知道写了多少个块,关键是,还可以知道写了哪些块。它对应写IO的块数。
写IO的次数,当然就是调用OS中的异步IO命令了,在Solaris中,是aiowrite。通过它,可以知道写IO的次数。这个是OS依赖函数,不同OS中会不一样。kcbhpbwt在所有OS中都是一样的。
后一个批数,就是Batch数,要观察等待事件了。这个要观察memcpy函数,每有一次等待事件,Oracle会像一处内存中拷贝48字节的内存。我们可以只观察memcpy第三个参数为48的调用,这就对应一次等待事件了。它的第二个参数是,要拷贝的源内存地址,它的前四个字节,是等待事件的event#,通过这个,我们可以知道等待事件是什么。
我的观察脚本是这样的,
bash-3.2# more dbwr2.d
#!/usr/sbin/dtrace -s -n
char *memnr;
int latchaddr;
dtrace:::BEGIN
{
i=1;
latchaddr=0;
}
pid$1::kcbhpbwt:entry
{
memnr=copyin(arg0+4,8);
printf("|%2x.%2x.%2x.%2x|",memnr[3],memnr[2],memnr[1],memnr[0]);
printf("i=%d PID::entry:==%s:%s:%s:%s %x %x %x %x %x %x ",i, probeprov, probemod, probefunc, probename, arg0,arg1,arg2,arg3,arg4,arg5);
i=i+1;
}
pid$1::aiowrite:entry
{
printf("i=%d PID::entry:==%s:%s:%s:%s %x %x %d %x %x %x",i, probeprov, probemod, probefunc, probename,arg0,arg1,arg2/8192,arg3,arg4,arg5);
i=i+1;
}
pid$1::aiowait:entry
{
printf("i=%d PID::entry:==%s:%s:%s:%s %x %x %d %x %x %x",i, probeprov, probemod, probefunc, probename,arg0,arg1,arg2/8192,arg3,arg4,arg5);
i=i+1;
}
pid$1::memcpy:entry
/ arg2==48 /
{
memnr=copyin(arg1,25);
printf("%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
memnr[0],memnr[1],memnr[2],memnr[3],memnr[4],memnr[5],memnr[6],memnr[7],memnr[8],memnr[9],
memnr[10],memnr[11],memnr[12],memnr[13],memnr[14],memnr[15],memnr[16],memnr[17],memnr[18],memnr[19]);
printf("i=%d PID::entry:==%s:%s:%s:%s %x %x %x %x %x %x",i, probeprov, probemod, probefunc, probename,arg0,arg1,arg2,arg3,arg4,arg5);
i=i+1;
}
可以ps -ef|grep dbw查到dbwr的进程号,用“./dbwr2.d -p DBWR进程号”的方式,调用这个脚本。
脚本结果类似这样:
bash-3.2# more dbwr1.log
CPU ID FUNCTION:NAME
1 51325 memcpy:entry 7 0 0 0 0 0 0 02c 1 0 0 0 0 0 02c 1 0 0i=1 PID::entry:==pid1134:libc.so.1:memcpy:entry 3954446b8 fffffd7fffdfe328 30 88d6 c
c0adb0 0
0 51321 kcbhpbwt:entry | 0.c0.69.8f|i=2 PID::entry:==pid1134:oracle:kcbhpbwt:entry 38879a000 2000 c0698f 1 0 fffffd7ffcb36330
0 51321 kcbhpbwt:entry | 0.c0.69.90|i=3 PID::entry:==pid1134:oracle:kcbhpbwt:entry 388368000 2000 c06990 1 0 540
0 51321 kcbhpbwt:entry | 0.c0.69.91|i=4 PID::entry:==pid1134:oracle:kcbhpbwt:entry 389978000 2000 c06991 1 0 540
0 51321 kcbhpbwt:entry | 0.c0.69.92|i=5 PID::entry:==pid1134:oracle:kcbhpbwt:entry 388c42000 2000 c06992 1 0 540
kcbhpbwt后面的0.c0.69.8f,是DBA。此处DBWR个要写的脏块,是3号文件27023号块。
开头的等待事件,它的event#是7,也就是rdbms ipc message事件。
相关文章