2013,新春巨献,如何提高DBWR的写性能,减少db file parallel write等待

2020-06-28 00:00:00 测试 等待 事件 次数 研究
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事件。

相关文章