作者:dbtan |【转载时请以超链接形式标明文章出处作者信息】


Buffer Cache的原理及使用:

Buffer Cached 的原理及使用

⑴ 当一个Server进程需要读数据到Buffer Cache中时,首先必须判断该数据在Buffer中是否存在(图①所示过程),如果存在且可用,则获取该数据,同时根据LRU算法增进其访问计数;如果Buffer中不存在该数据,则需要从数据文件上进行读取。

⑵ 在读取数据之前,Server进程需要扫描LRU List寻找Free的Buffer,扫描过程中Server进程会把发现的所有已经被修改过的Buffer注册到Dirty List上(图中②所示过程),这些Dirty Buffer随后可以被写出到数据文件。

⑶ 如果Dirty Queue超过了阀值,Server进程就会通知DBWn去写出脏数据(图中③所示过程)。
    这也是触发DBWn写的一个条件,这个阀值曾经提到是25%,也就是当Dirty Queue超过25%满就会触发DBWn的写操作:

sys@CCDB> col KVITTAG for a15
sys@CCDB> 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等待:

sys@CCDB> 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 注册到Dirty List以及Checkpoint Queue,这个扫描也受一个内部约束,在Oracle9iR2中,这个比例是25%。

⑷ 找到足够足够的Buffer之后,Server进程就可以将Buffer从数据文件读入Buffer Cache(图中④所示过程)。

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

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

可以通过如下命令转储Buffer Cache的内容,从而清晰地看到以上描述的数据结构:

alter session set events 'immediate trace name buffers level 4';

不同level转储的内容详细程度不同,此命令的可用级别主要有1~10级,其中各级别的含义如下。
·Level 1:仅包含Buffer Headers信息。
·Level 2:包含Buffer Headers和Buffer概要信息转储。
·Level 3:包含Buffer Headers和完整Buffer内容转储。
·Level 4:Level 1 + Latch转储 + LRU队列。
·Level 5:Level 4 + Buffer概要信息转储。
·Level 6 和Level 7:Level 4 +完整的Buffer内容转储。
·Level 8:Level 4 +显示users/waiters信息。
·Level 9:Level 5 +显示users/waiters信息。
·Level 10:Level 6 +显示users/waiters信息。

转储仅限于在测试环境中使用,转储的跟踪文件可能非常巨大,为获取完整的跟踪文件,建议设置初始化参数max_dump_file_size为UNLIMITED。

本文选取环境为:
sys@CCDB> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
PL/SQL Release 11.1.0.6.0 - Production
CORE    11.1.0.6.0      Production
TNS for Linux: Version 11.1.0.6.0 - Production
NLSRTL Version 11.1.0.6.0 - Production

主要相关参数设置为:
sys@CCDB> show parameter max_dump

NAME                                 TYPE                             VALUE
------------------------------------ -------------------------------- ------------------------------
max_dump_file_size                   string                           unlimited

sys@CCDB> show parameter memory_target

NAME                                 TYPE                             VALUE
------------------------------------ -------------------------------- ------------------------------
memory_target                        big integer                      804M

从Level 4级跟踪文件的开头部分可以获得如下信息,这是记录的不同List的Prev和Next定位信息。其中WS就是指Working Sets,注意WSID指不同WS的编号:

[oracle@test7: ~/diag/rdbms/ccdb/ccdb/trace]$less ccdb_ora_5176.trc
Dump of buffer cache at level 4 for tsn=2147483647, rdba=0
  (WS) size: 0 wsid: 1 state: 0
    (WS_REPL_LIST) main_prev: 0x8ec892d0 main_next: 0x8ec892d0 aux_prev: 0x8ec892e0 aux_next: 0x8ec892e0curnum: 0
auxnum: 0
cold: 8ec892d0 hbmax: 0 hbufs: 0
    (WS_WRITE_LIST) main_prev: 0x8ec89300 main_next: 0x8ec89300 aux_prev: 0x8ec89310 aux_next: 0x8ec89310curnum: 0
auxnum: 0
    (WS_XOBJ_LIST) main_prev: 0x8ec89330 main_next: 0x8ec89330 aux_prev: 0x8ec89340 aux_next: 0x8ec89340curnum: 0
auxnum: 0
    (WS_XRNG_LIST) main_prev: 0x8ec89360 main_next: 0x8ec89360 aux_prev: 0x8ec89370 aux_next: 0x8ec89370curnum: 0
auxnum: 0
    (WS_REQ_LIST) main_prev: 0x8ec89390 main_next: 0x8ec89390 aux_prev: 0x8ec893a0 aux_next: 0x8ec893a0curnum: 0 a
uxnum: 0
  (WS) fbwanted: 0
  (WS) bgotten: 0 sumwrt: 0
  (WS) pwbcnt: 0

接下来是具体的List链表信息,注意这里存在多条NULL列表,这是为Buffer Cache不同部分(Keep池、Recycle池以及不同block_size大小的内存使用)预分配的List:

MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
AUXILIARY RPL_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARY RPL_LST Queue header (PREV_DIRECTION)[NULL]
MAIN WRT_LST Queue header (NEXT_DIRECTION)[NULL]
MAIN WRT_LST Queue header (PREV_DIRECTION)[NULL]
AUXILIARY WRT_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARY WRT_LST Queue header (PREV_DIRECTION)[NULL]
MAIN XOBJ_LST Queue header (NEXT_DIRECTION)[NULL]
MAIN XOBJ_LST Queue header (PREV_DIRECTION)[NULL]
AUXILIARY XOBJ_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARY XOBJ_LST Queue header (PREV_DIRECTION)[NULL]
MAIN XRNG_LST Queue header (NEXT_DIRECTION)[NULL]
MAIN XRNG_LST Queue header (PREV_DIRECTION)[NULL]
AUXILIARY XRNG_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARY XRNG_LST Queue header (PREV_DIRECTION)[NULL]
MAIN REQ_LST Queue header (NEXT_DIRECTION)[NULL]
MAIN REQ_LST Queue header (PREV_DIRECTION)[NULL]
AUXILIARY REQ_LST Queue header (NEXT_DIRECTION)[NULL]
AUXILIARY REQ_LST Queue header (PREV_DIRECTION)[NULL]
  (WS) size: 0 wsid: 2 state: 0
    (WS_REPL_LIST) main_prev: 0x8ec89828 main_next: 0x8ec89828 aux_prev: 0x8ec89838 aux_next: 0x8ec89838curnum: 0
auxnum: 0
cold: 8ec89828 hbmax: 0 hbufs: 0
    (WS_WRITE_LIST) main_prev: 0x8ec89858 main_next: 0x8ec89858 aux_prev: 0x8ec89868 aux_next: 0x8ec89868curnum: 0
auxnum: 0
    (WS_XOBJ_LIST) main_prev: 0x8ec89888 main_next: 0x8ec89888 aux_prev: 0x8ec89898 aux_next: 0x8ec89898curnum: 0
auxnum: 0
    (WS_XRNG_LIST) main_prev: 0x8ec898b8 main_next: 0x8ec898b8 aux_prev: 0x8ec898c8 aux_next: 0x8ec898c8curnum: 0
auxnum: 0
    (WS_REQ_LIST) main_prev: 0x8ec898e8 main_next: 0x8ec898e8 aux_prev: 0x8ec898f8 aux_next: 0x8ec898f8curnum: 0 a
uxnum: 0
  (WS) fbwanted: 0
  (WS) bgotten: 0 sumwrt: 0
  (WS) pwbcnt: 0

从以上输出还可以看到,Buffer Cache中除了RPL_LST和WRT_LST外还存在其他分类的List,作用各不相同。Buffer Cache的多缓冲池以及WS结构如下图所示。

Buffer Cache 的多缓冲池以及多WS结构

同时在Level 4级的转储中,再向下可以看到主要RPL_LST的队列信息,这也是链表的一个最直观表现:

MAIN RPL_LST Queue header (NEXT_DIRECTION)[0x72ffbf68,0x74fedef8]
0x72ffbed8=>0x733d84d8=>0x733d8998=>0x733d8ac8=>0x733d8f88=>0x733d97d8=>0x733d9c98=>0x733d9dc8
0x733da4e8=>0x733da9a8=>0x733daad8=>0x733dad38=>0x733dae68=>0x733db0c8=>0x733db458=>0x733db588
0x733db7e8=>0x733db918=>0x733dba48=>0x733dbca8=>0x733dbf08=>0x733dc038=>0x733dc168=>0x733dc3c8
0x733dc628=>0x733dc888=>0x733dcae8=>0x733dcc18=>0x733dd208=>0x733dd468=>0x733dd6c8=>0x733dd928
0x733ddde8=>0x733de048=>0x733de178=>0x733de768=>0x733de9c8=>0x733deaf8=>0x733dec28=>0x733ded58
0x733dee88=>0x733defb8=>0x733df478=>0x733df5a8=>0x733df808=>0x733df938=>0x733dfcc8=>0x733e0058

- The End -