原文出处:http://www.itpub.net/thread-1631537-1-1.html http://www.linuxidc.com/Linux/2012-07/66767.htm 感谢vage兄
mian repl_lst , aux repl_lst
oracle buffer cache中辅助链表的定位就是让进程快速的找到可用buffer,所有全表扫描的块都被放到辅助链表。
非全表扫描,先到辅助LRU中寻找可有块,找到后会将其从辅助LRU中,移到主LRU冷端头。
如果非全表扫描较多,辅助LRU中块会越来越少,为了保持比例(辅助LRU占整个LRU总块数的20%到25%左右),SMON进程会3秒一次持有LRU
Latch,将主LRU冷端末的块移到辅助LRU。
全表扫描也先到辅助LRU中寻找可用块,但全表扫描的块仍将留在辅助LRU,不会调往主LRU冷端头。因此全表扫描的块将很快被覆盖。全表
扫描操作将只使用辅助LRU(也会用到主LRU,只会用到很少量的主LRU),一次大的全扫操作,可以将辅助LRU的所有块覆盖一遍或多遍。
数据库刚启动时,或刚Flush Buffer_cache时,所有块会被放在辅助LRU中。
前台进程(服务器进程)扫描主、辅LRU时,会将遇到的TCH为2以下的脏块,移到主LRUW。
DBWR三秒一次,扫描检查点队列的长度、确定是否要写脏块。另外,DBWR每三秒还会扫描主LRUW,将其中的脏块移到辅助LRUW,然后写到磁盘。
从检查点队列写到磁盘中的块,不会改变它在LRU链表中的位置。从LRUW写到磁盘中的块,会被放于辅助LRU,以供马上覆盖。
select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE from x$kcbwds; CNUM_SET CNUM_REPL ANUM_REPL CNUM_WRITE ANUM_WRITE 4710 4710 4704 0 0 4710 4710 4704 0 0
SQL> select lru_flag,count(*) from x$bh group by lru_flag; LRU_FLAG COUNT(*) ---------- ---------- 6 3081 2 6734 8 2 0 2658 LRU_FLAG为6、4,说明BUffer在辅助LRU中。为0、2,说明在主LRU的冷端,为8、9的,说明在主LRU的热端
x$kcbwds视图中CNUM_SET列,统计工作组所有块数量。简单点说,也就是LRU链上所有块的数量。
CNUM_REPL列是主、辅LRU链表中所有的块数。
ANUM_REPL列是辅LRU链上所有的块数。
用CNUM_REPL-ANUM_REPL,即为所有主LRU链上的块数。
CNUM_WRITE、ANUM_WRITE这个列对应LRUW(也即为脏LRU)链,CNUM_WRITE为LRUW链中所有块数,ANUM_WRITE为辅助LRUW中的块数。两个相减,要以得到主LRUW中的块数。
脏块何时进入LRUW队列?
1、DBWR 每3秒醒来,之后它会做两件事:
(1)、检查检查点队列长度,如果脏块太多、恢复时间有可能超过fast_start_mttr_target参数的值,开始沿着检查点队列写脏块。
(2)、检查LRUW,有脏块就写。
2、当前台进程(也就是服务器进程)扫描LRU时,会将发现的脏块移到LRUW中,等待3秒一次DBWR醒来,将脏块写磁盘。
前面说的流行说法还有第三点:
3、当前台进程扫描LRU的一定数量的块后,都没有发现可用块,此时会触发紧急写。前台进程等待,DBWR将脏块从检查点队列移到LRUW,从LRUW写到磁盘。
这种说法容易理解,这是紧急情况。前台进程已经在等待Free Buffer Waits了,DBWR不会再按检查点队列依次写,而是从LRU中碰到脏块就移到LRUW、然后写到磁盘。
select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE from x$kcbwds where cnum_set>0;
select * from v$sysstat where name in ('dirty buffers inspected');
第一条语句可以查看LRUW的长度信息,第二条语句,可以观察前台进程在扫描LRU时,跳过的脏块数。我们可以观察到如下结果:
rowid与file#,block_id换算
SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid),dbms_rowid.rowid_block_number(rowid) from XX ;
一个Checkpoint Queue,一个Lruw
脏块通过CKPT-Q写到磁盘后,其所处的LRU位置不变,这一点我在前文中已经提到过,也很容易验证这点,从x$BH中的 NXT_REPL,PRV_REPL两列,就可以验证此点。也就是说,从CKPT-Q写脏块,是和LRU链表无关的,也就是不需要获得LRU Latch。如果从CKPT-Q写脏块申请了LRU Latch,哪一定和LRUW有关。
将检查点超时参数设为很小的值,写个简单的DTrace脚本,跟踪一下DBWR进程Latch的获得情况。发现每次从CKPT-Q写脏块时,DBWR都要按如下顺序申请Latch:
获得cache buffers chains Latch
获得LRU Latch
释放LRU Latch
释放cache buffers chains Latch
获得checkpoint queue latch
释放checkpoint queue latch
获得cache buffers lru chain
释放cache buffers lru chain
也就是说,从CKPT-Q写脏块时,不但要获得checkpoint queue latch,还要LRU Latch。根据前面的分析,从CKPT-Q写脏块时,获取LRU Latch的目的,只能是为了访问LRUW,因为CKPT-Q写不改变块在LRU的位置,不必要访问LRU。哪么,CKPT-Q写访问LRUW的目的是什 么,可以推论,目的是为了检查脏块是否在LRUW、并摘掉它。
两个链表设计的目的是不一样的,Checkpoint Queue按照数据块第一次被修改的先后时间排序(数据块只要变脏,肯定在这个链表上),Dbwr沿着Checkpoint Queue写脏数据,Oracle希望越早修改的块,会越早被写出。有利于减少恢复时间。但是有一点非常重要,这些块被写到磁盘后,1)是不会改变这个块 在Lru链表里的位置的 2)这个块依然存在在内存里(很多人有误解,认为写出脏快后,这个块就不在内存里了)。而Lruw链表不是这个目的,它的设计只是为了腾出点空间来,让后 面发生物理读的块或者需要构造CR的块可以在Buffer Cache里有地方,既然是为了腾出空间,那么就要有块被牺牲掉挪出空间来,一般是Tch小于2,CR块,全表扫描的块会被牺牲重用。在系统急需大量空闲 块的时候,进程搜索LRU链表(包含Lru-Aux)过程中,如果发现了脏快(因为是从Lru冷端扫起,所以脏快的Tch也很小)那么就挪到Lruw链表 上,然后再从Lruw写入到磁盘,最终这个Free的内存块就可以被挪到Lru-Aux上被重用了,当然这个块被写入磁盘后,需要把这个块从 Checkpoint Queue摘除。一个块被修改变脏后,一定会进入到Checkpoint Queue队列,但是不会立即进入到Lruw队列,只有发生进程搜索LRU链表的时候,如果Tch数比较小,才会被放入Lruw链表然后被写入磁盘。并不 象很多人认为的,Lru链表里就应是干净的块。其实Lru链表里的脏快应该是很多的,特别是如果你的脏块访问的比较频繁,Tch数比较高,是很难被刷到 Lruw链表里的,它会一直呆在Lru连表里,当然这个脏块最终会从Checkpoint Queue写出变为干净的块,但是它在Lru链表里的位置不变,不会被刷出Buffer Cache。