buffer cache 的组织方式

buffer cache 的组织方式:

1、LRU与Dirty List

在Buffer Cache中,Oracle通过几个链表进行内存管理。
LRU list用于维护内存中的Buffer,按照LRU算法进行管理。数据库初始化时,所有的Buffer都被Hash到LRU list上管理。
当需要从数据文件上读取数据时,首先要在LRU List上寻找Free的Buffer,然后读取数据到Buffer Cache中;
当数据被修改之后,状态变为Dirty,就可以被移动至Dirty List,Dirty List上的都是候选的可以被DBWR写出到数据文件的Buffer,
一个Buffer要么在LRU List上,要么在Dirty List上存在,不能同时存在于多个list。

Cache Buffers Lru Chain闩锁竞争与解决
当进程需要读数据到Buffer Cache时,或Cache Buffer根据LRU算法进行管理时,就不可避免地要扫描LRU List
获取可用Buffer或更改Buffer状态。Oracle的Buffer Cache是共享内存,可以为众多并发进程并发访问,
所以在搜索过程中必须获取Latch(Latch是Oracle的一种串行锁机制,用于保护共享内存结构),
锁定内存结构,防止并发访问损坏内存中的数据。
这个用于锁定LRU的Latch就是经常见到的Cache Buffers Lru Chain:

SQL> select addr,latch#,name,gets,misses,immediate_gets,immediate_misses
     from v$latch where name='cache buffers lru chain'; 
     
Cache Buffers Lru Chain Latch存在多个子Latch,其数量受隐含参数_db_block_lru_latches控制。

可以从v$latch_children视图查看当前各子Latch使用情况:

SQL> select addr,child#,name,gets,misses,immediate_gets,immediate_misses
     from v$latch_children where name='cache buffers lru chain';     
    
2、Hash Bucket和Cache Buffer Chain

Oracle引入了Bucket的数据结构,Oracle把管理的所有Buffer通过一个内部的Hash算法运算后,存放到不同的Hash Bucket中,
这样通过Hash Bucket分割之后,众多的Buffer被分布到一定数量的Bucket之中,当用户需要在Buffer中定位数据是否存在时,
只需要通过同样的算法获得Hash值,然后到相应得Bucket中查找少量的Buffer即可确定。
Bucket内部通过Cache Buffer Chain(Cache Buffer Chain是一个双向链表)将所有的Buffer通过Buffer Header信息联系起来。
Buffer Header存放的是对应数据块的概要信息,包括数据块的文件号、块地址、状态等。要判断数据块在Buffer中是否存在,
检查Buffer Header即可确定。
对应每个Bucket,只存在一个Chain,当用户试图搜索Cache Buffer Chain时,必须首先获得Cache Buffer Chain Latch。 

①从Oracle 8i开始,Bucket的数量比以前大大增加;通过增加的Bucket的“稀释”使得每个Bucket上的Buffer数量大大减少。
②在Oracle 8i之前,_db_block_hash_latches的数量和Bucket的数量是一致的,每个Latch管理一个Bucket;
从Oracle 8i开始每个Latch需要管理多个Bucket,由于每个Bucket上的Buffer数量大大降低,所以Latch的性能反而得到提高。
③每个Bucket存在一条Cache Buffer Chain。
④Buffer Header上存在指向具体Buffer的指针

要想访问bucket 桶里的buffer header 必须先获得cache buffer chain latch,当访问莫个数据块过多时,就出现cache buffer chain latch
的争用。
cache buffer chain latch ---->  bucket ------> buffer header -----> buffer cache

cache buffer chain latch 其数量受隐含参数__db_block_hash_latches控制
bucket                   其数量受隐含参数_db_block_hash_buckets 控制

Buffer Header数据可以从数据库的字典表中查询得到,这张字典表是X$BH。每个Buffer在X$BH中都存在一条记录。
select count(*) from X$BH;


总结:

buffer cache的组织方式有两种:(这两种方式是独立的)
一种是hash buckets/chains方式,这种方式的目的是为了查找specific cached data block(特定的缓存块)
另外一种方式LRU链表方式,是为查找到空块(find any free buffer)

通过LRU list, 是在物理读或构造CR块是找可用Buffer。
bucket的latch,是逻辑读时定位Buffer Header。
也就是说在正常的buffer 操作中,基本上是通过bucket的方式,只有在物理读取的时候使用到LRU。

cache buffer chain latch 其数量受隐含参数__db_block_hash_latches控制
bucket                   其数量受隐含参数_db_block_hash_buckets 控制
Buffer Header数据可以从数据库的字典表中查询得到,这张字典表是X$BH。每个Buffer在X$BH中都存在一条记录。
select count(*) from X$BH;
Cache Buffers Lru Chain Latch存在多个子Latch,其数量受隐含参数_db_block_lru_latches控制。

解决LRU LATCH 问题:
 
如果该Latch竞争激烈,通常有如下方法可以采用:
适当增大Buffer Cache,这样可以减少读数据到Buffer Cache的机会,减少扫描LRU List的竞争;
可以适当增加LRU Latch数量,修改_db_block_lru_latches参数,该方法不推荐使用;
通过多缓冲池技术,可以减少不希望的数据老化和全表扫描等操作对Default池的冲击,从而可以减少竞争。

解决 cache buffer chain latch 竞争的问题

X$BH中有一个重要字段TCH,TCH为Touch的缩写,表示一个Buffer的访问次数,Buffer被访问的次数越多,说明该Buffer越“抢手”,也就可能存在热点块竞争的问题。

1、热点块的优化(通过X$BH查看访问比较多块)
可以通过以下查询获得当前数据库最繁忙的Buffer
SQL>
select *
from (select addr,ts#,file#,dbarfil,dbablk,tch
from x$bh order by tch desc)
where rownum<11;

再结合dba_extents中的信息,可以查询到这些热点Buffer都来自哪些对象:
SQL> 
select e.owner,e.segment_name,e.segment_type
from dba_extents e,
   (select *
    from (select addr,ts#,file#,dbarfil,dbablk,tch
          from x$bh order by tch desc)
    where rownum<11) b
where e.relative_fno=b.dbarfil
and e.block_id<=b.dbablk
and e.block_id+e.blocks>b.dbablk;

对于已经知道sid的情况,自己查询热点对象
 SELECT OBJ, OBJECT_NAME, TCH, TIM
FROM X$BH A, DBA_OBJECTS B
WHERE HLADDR IN (SELECT P1RAW FROM V$SESSION_WAIT WHERE SID = 627)
AND A.OBJ = B.DATA_OBJECT_ID;

要解决热点块的问题,可以通过将热点块中的行分散到多个数据块中去,
这样原来的热点块就变成了多个数据块,这样被hash到同一个latch的几率就降低了。
如果热点块属于表,则可以先将表的数据导出来,然后增加表的pctfree值,最后将数据再导入。
如果热点块属于索引,则可以设定较高的 pctfree参数后,重建索引。注意,这会增加索引的高度。

2、不够优化的SQL语句是导致cache buffers chains latch的主要原因。
如果SQL语句需要访问过多的内存数据块,那么必然会持有latch很长时间。找出逻辑读特别大的sql语句进行调整。
查找cache buffers chains latch 竞争的sql语句
select /*+rule*/ hash_value,sql_text 
       from v$sqltext 
       where (hash_value,address) in (
              select a.hash_value,a.address 
              from v$sqltext a,
                   (select distinct a.owner,a.segment_name,a.segment_type
                               from dba_extents a,
                               (select dbarfil,dbablk 
                                  from (select dbarfil,dbablk 
                                   from x$bh 
                                   order by tch desc) 
                                where rownum<11) b
              where a.RELATIVE_FNO = b.dbarfil 
                 and a.BLOCK_ID <= b.dbablk 
                 and a.block_id + a.blocks > b.dbablk) b
       where upper(a.sql_text) like '%' || b.segment_name || '%' 
       and b.segment_type='TABLE') 
order by hash_value,address,piece;      

猜你喜欢

转载自blog.csdn.net/2301_76957510/article/details/129852207