ITPub博客

首页 > 数据库 > Oracle > Buffer cache原理

Buffer cache原理

原创 Oracle 作者:Damon__Li 时间:2014-12-05 16:26:55 0 删除 编辑

Buffer Cache中,Oracle通过几个链表进行内存管理

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

Buffer Cache的原理及使用

    ①当一个Server进程需要读数据到Buffer Cache中时,首先必须判断该数据在Buffer中是否存在,如果存在且可用,则获取该数据,根据LRU算法在LRU List上移动该Block;如果Buffer中不存在数据,则需要从数据文件上读取。

    ②在读取数据之前,Server进程需要扫描LRU List寻找FreeBuffer,扫描过程中Server进程会把发现的所有已经被修改过的Buffer移动到Checkpoint Queue上,这些Dirty Buffer随后可以被写出到数据文件。

    ③如果Checkpoint Queue超过了阈值,Server进程就会通知DBWn去写出脏数据;这也是触发DBWn写的一个条件,这个阈值曾经提到是25%,也就是当检查点队列超过25%满就会触发DBWn的写操作:

                SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbldq';

                            KVITTAG            KVITVAL KVITDSC

                        -----------------------------------------------------------------

                            kcbldq                  25 large dirty queue if kcbclw reaches this

    ④如果Server进程扫描LRU超过一个阈值仍然不能找到足够的Free Buffer,将停止寻找,转而通知DBWn去写出脏数据,释放内存空间。这个数字是40%,也就是说当Server进程扫描LRU超过40%还没能找到足够
Free Buffer就会停止搜索,通知DBWn执行写出,这时进程会处于free buffer wait等待:

                SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbfsp';

                            KVITTAG      KVITVAL KVITDSC

                        ---------- ---------- -------------------------------------------------------

                            kcbfsp            40 Max percentage of LRU list foreground can scan for free

    ⑤同时由于增量检查点的引入,DBWn也会主动扫描LRU List,将发现的Dirty Buffer移至Checkpoint Queue,这个扫描也受到一个内部约束,在Oracle 9iR2中这个比例是25%

            SQL> Select kvittag,kvitval,kvitdsc from x$kvit where kvittag='kcbdsp';

                        KVITTAG      KVITVAL KVITDSC

                        ---------- ---------- -------------------------------------------------------

                        kcbdsp            25 Max percentage of LRU list dbwriter can scan for dirty

 

    找到足够的Buffer之后,Server进程就可以将Buffer从数据文件读入Buffer Cache

    如果读取的Block不满足读一致性要求,则Server进程需要通过当前Block版本和回滚段构造前镜像返回给用户。

    从Oracle 8i开始,LRU ListDirty List又分别增加了辅助ListAuxiliary list用于提高管理效率。引入了辅助List之后,当数据库初始化时,Buffer首先存放在LRU的辅助List上(Auxiliary rpl_lst),当被使用后移动到LRU的主List上(Main rpl_lst),这样当用户进程搜索Free Buffer时,就可以从LRU-AUX List开始,而DBWR搜索Dirty Buffer时,则可以从LRU-MAIN List开始,从而提高了搜索效率和数据库性能。

 

Cache Buffers Lru Chain闩锁竞争与解决

    当进程需要读数据到Buffer Cache时,或Cache Buffer根据LRU算法进行管理时,就不可避免地要扫描LRU List获取可用Buffer或更改Buffer状态。OracleBuffer Cache是共享内存,可以为众多并发进程并发访问,所以在搜索过程中必须获取LatchLatchOracle的一种串行锁机制,用于保护共享内存结构),锁定内存结构,防止并发访问损坏内存中的数据。

这个用于锁定LRULatch就是经常见到的Cache Buffers Lru Chain

SQL> select addr,latch#,name,gets,misses,immediate_gets,immediate_misses

 2 from v$latch where name='cache buffers lru chain';

ADDR             LATCH#        NAME                             GETS        MISSES      IMMEDIATE_GETS        IMMEDIATE_MISSES

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

01FED330        92              cache buffers lru chain       12409             3                            5849                                             0

 

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

 2 from v$latch_children where name='cache buffers lru chain';

 

ADDR        CHILD# NAME                           GETS    MISSES   IMMEDIATE_GETS IMMEDIATE_MISSES

-------- ---------- ------------------------- ---------- ----------  -------------- ----------------

67EA55E8         8 cache buffers lru chain          22         0     0               0

67EA511C         7 cache buffers lru chain          22         0    0               0

67EA4C50         6 cache buffers lru chain          22         0     0               0

67EA4784         5 cache buffers lru chain          22         0      0               0

67EA42B8         4 cache buffers lru chain          22         0     0               0

 67EA3DEC         3 cache buffers lru chain       12553         3   6137               0

67EA3920         2 cache buffers lru chain          22         0    0               0


67EA3454         1 cache buffers lru chain          22         0   0               0

 

如果该Latch竞争激烈,通常有如下方法可以采用:

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


Cache Buffer Chain闩锁竞争与解决

    在LRUDirty List这两个内存结构之外,Buffer Cache的管理还存在另外两个重要的数据结构:Hash BucketCache Buffer Chain

可以想象如果所有的Buffer Cache中的所有Buffer都通过同一个结构管理,当需要确定某个BlockBuffer中是否存在时,将需要遍历整个结构,性能会相当低下。

为了提高效率,Oracle引入了Bucket的数据结构,Oracle把管理的所有Buffer通过一个内部的Hash算法运算后,存放到不同的Hash Bucket中,这样通过Hash Bucket分割之后,众多的Buffer被分布到一定数量的Bucket之中,当用户需要在Buffer中定位数据是否存在时,只需要通过同样的算法获得Hash值,然后到相应得Bucket中查找少量的Buffer即可确定。

Bucket内部通过Cache Buffer ChainCache 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的指针。

X$BHBuffer Header

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

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

 

可以通过以下查询获得当前数据库最繁忙的Buffer

SQL> select * from (select addr,ts#,file#,dbarfil,dbablk,tch from x$bh order by tch desc) where rownum<11;

 

ADDR           TS#     FILE#   DBARFIL    DBABLK       TCH

-------- ---------- ---------- ---------- ---------- ----------

0430A6E8         0         1         1      1498       652

0430C9FC         0         1         1      7882       142

0430A5A4         0         1         1      7938       142

0430C9FC         0         1         1      8202       142

0430C9FC         0         1         1      7873       107

0430C9FC         0         1         1      7889       107

0430C9FC         0         1         1      8233       107

0430C9FC         5         5         5     18444       106

0430C9FC         5         5         5     18516       106

0430C9FC         0         1         1      9017       106

 

再结合dba_extents中的信息可以查询到这些热点Buffer都来自哪些对象

SQL> select e.owner,e.segment_name,e.segment_type

 2 from dba_extents e,

 3 (select *

 4 from (select addr,ts#,file#,dbarfil,dbablk,tch

 5 from x$bh order by tch desc)

 6 where rownum<11) b

 7 where e.relative_fno=b.dbarfil

 8 and e.block_id<=b.dbablk

 9 and e.block_id+e.blocks>b.dbablk;

OWNER          SEGMENT_NAME             SEGMENT_TYPE

--------------- ------------------------- -------------------------

SYS            I_JOB_NEXT               INDEX

SYS            I_FILE#_BLOCK#           INDEX

SYS            C_FILE#_BLOCK#           CLUSTER

SYS            I_FILE#_BLOCK#           INDEX

SYS            C_USER#                  CLUSTER

SYS            I_OBJ1                   INDEX

SYS            OBJ$                     TABLE

SYS            OBJ$                     TABLE

SYS            C_FILE#_BLOCK#           CLUSTER

SYS            C_TS#                    CLUSTER






来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25725504/viewspace-1357904/,如需转载,请注明出处,否则将追究法律责任。

下一篇: crontab命令
请登录后发表评论 登录
全部评论

注册时间:2012-04-25

  • 博文量
    25
  • 访问量
    61205