在前面文章介绍LRU链表和DBWR进程的时候,没有说到latch,这里单独拿出来说一下,本文只说明与buffer cache相关的latch,等待事件latch free中与buffer cache有关的有两种:cache buffers chains latch 和 cache buffers lru chain latch。看过前面介绍LRU链表和DBWR进程以后,这2个latch就比较好理解了。
对于buffer cache中的每个hash chain链表来说,都会由一个cache buffers chains latch的latch来保护对hash chain的并发操作,这种latch还有另一个名字:hash latch或者CBC latch。当然一个数据库中有很多个cache buffers chains latch,每个latch都叫做child cache buffers chains latch,每个child cache buffers chains latch会管理多个hash chain。cache buffers chains latch的数量由一个隐藏参数:_db_block_hash_latches来决定,而该隐藏参数的缺省值时候buffer cache中所含有的内存数据块的多少来决定的,满足如下公式:
·少于2052个时,_db_block_hash_latches = power(2,trunc(log(2,内存块数量- 4) – 1))
·多于131075个时,_db_block_hash_latches = power(2,trunc(log(2, db_block_buffers – 4) – 6))
·位于2052与131075 buffers之间,_db_block_hash_latches = 1024
可以通过如下sql来查询当前系统的cache buffers chains latch的数量:
1 2 3 4 5 6 7 8 9 10 11 |
SQL> select count(distinct(hladdr)) from x$bh; COUNT(DISTINCT(HLADDR)) ----------------------- 2048 SQL> select count(*) from v$latch_children where name='cache buffers chains'; COUNT(*) ---------- 2048 |
既然知道了cache buffers chains latch 的数量,那么我们就可以算出每个latch管理多少个hash chain了,计算公式:hash chain的数量除以latch的数量:
1 2 3 4 5 6 7 8 9 |
SQL> select x.ksppinm, y.ksppstvl, x.ksppdesc 2 from x$ksppi x , x$ksppcv y 3 where x.indx = y.indx 4 and x.ksppinm like '\_%' escape '\' 5 and ksppinm like '%_db_block_hash_buckets%'; KSPPINM KSPPSTVL KSPPDESC ------------------------------ -------------------- -------------------------------------------------- _db_block_hash_buckets 65536 Number of database block hash buckets |
通过如上查询所得:
每个latch管理hash chain的数量=65536/2048=32.
当数据库在搜索和扫描hash chain的过程中,需要先获得cache buffers chains latch,并一直持有,直到找到所有的数据块才会释放该latch,当一个进程一直在扫描一个hash chain的时候,此时如果另一个进程也想扫描同一个hash chain,那这个进程就必须等待类型为cache buffers chains latch的latch free等待事件。
一般出现cache buffers chains latch,很多情况下是由sql导致的,如果sql语句需要访问过多的内存数据块,那么这个latch就会持有比较长的时间。这时就需要找出逻辑读比较大的sql语句,看是否能进行调整,在v$sqlarea视图中,buffer_gets/executions比值比较大的sql语句,如果和其他sql语句出现比较大的反差,那就需要考虑这条sql语句是否可以优化了,我这里整理了一下用来查找比较一偶针对性的,查找这种引起较为严重的cache buffers chains latch的sql语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
SQL> select s.sql_text 2 from x$bh a,dba_extents b, 3 (select * 4 from (select addr 5 from v$latch_children 6 where name = 'cache buffers chains' order by sleeps desc) 7 where rownum<11) c, 8 v$sqltext s 9 where a.hladdr = c.addr 10 and a.dbarfil = b.relative_fno 11 and a.dbablk between b.block_id and b.block_id + b.blocks 12 and s.sql_text like '%'||b.segment_name||'%' and b.segment_type='TABLE' 13 order by s.hash_value,s.address,s.piece 14 / no rows selected |
引起cache buffers chains latch事件的还有另一个原因,那就是热点数据块的问题,指的就是多个session重复访问一个或者多个被同一个child cache buffers chains latch保护的内存数据块,可以通过如下sql来查找是否存在热点块:如果p1raw的值有很多,就说明session在等待同一个latch地址,系统存在热点块:
1 2 3 4 5 6 7 8 |
SQL> select sid, p1raw, p2, p3, seconds_in_wait, wait_time, state 2 from v$session_wait 3 where event = 'latch free' 4 order by p2, p1raw; no rows selected SQL> |
然后我们就可以根据p1raw的值去找到所对应的内存数据块以及对应的表的名称了:如下xxxxxxxx就是如上所查询出来的p1raw值,代入替换即可
1 2 3 4 |
select a.hladdr, a.file#, a.dbablk, a.tch, a.obj, b.object_name from x$bh a, dba_objects b where (a.obj = b.object_id or a.obj = b.data_object_id) and a.hladdr = 'xxxxxxxxxxx'; |
我们可以通过将热点块分散到多个数据块当中,这样原来的热点块就变成了多个数据块,被hash到同一个latch的几率就降低了,如果一个热点块属于表,则可以先将表导出,然后增加表的pctfree,再导入数据,如果热点块属于索引,则可以设定较高的pctfree,然后重建索引,这样可能会增加索引的高度。
通过前面我们已经知道,每个working set都会有一个名为cache buffers lru chain的latch(也叫做lru latch)来管理。任何要访问working set的进程都必须先获得cache buffers lru chain latch。cache buffers lru chain latch争用也是由于低效的扫描过多的内存数据块的SQL语句引起的。调整这些语句以降低逻辑读和物理读。只要修改一下上面找引起cache buffers chains latch的SQL语句即可找到这样的SQL语句。
- 本文固定链接: http://www.savedba.com/?p=497
- 转载请注明: 版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!