揭密Oracle之七种武器之四:揭密Buffer Cache中的链表

有日子没写东西了。又是看房又是讲课,好多朋友问我是不是不写了,怎么会呢,分享知识,也是自我总结的一个过程,对自己的提高也是有帮助的吧。
前段时间,一直有人问我Buffer Cache的链表,LRU、辅助LRU、检查点队列等等。检查点队列已经有很多文章讨论过了,我就不再重复的制造轮子
。 
另外,还有主LRU冷热端的相关内容,这一块我也不再详细描述,因为也有相关的文章。
我主要说一下主LRU、辅助LRU和LRUW相关的内容。
本篇文章没有使用DTrace和GDB,难度较低,但实验内容较多。我先将结果写在前面,如果没时间按个看实验,大概了解一下也行。
1、辅助LRU的定位,是让进程可以在辅助LRU中尽快找到可用块,因此,辅助LRU中存放的,都是相较主LRU,更加没有用的块。比如全表扫描的
块,都会被放在辅助LRU。另外,当空间紧张时,前台进程会将脏块移到LRUW,这些从LRUW写出的脏块,写完成后也会放入辅助LRU。这样辅助LRU中块更
多,前台进程更加容易在辅助LRU中找到可用块。
2、非全表扫描,先到辅助LRU中寻找可有块,找到后会将其从辅助LRU中,移到主LRU冷端头。
3、如果非全表扫描较多,辅助LRU中块会越来越少,为了保持比例(辅助LRU占整个LRU总块数的20%到25%左右),SMON进程会3秒一次持有LRU 
Latch,将主LRU冷端末的块移到辅助LRU。
4、全表扫描也先到辅助LRU中寻找可用块,但全表扫描的块仍将留在辅助LRU,不会调往主LRU冷端头。因此全表扫描的块将很快被覆盖。全表
扫描操作将只使用辅助LRU(也会用到主LRU,只会用到很少量的主LRU),一次大的全扫操作,可以将辅助LRU的所有块覆盖一遍或多遍。
5、数据库刚启动时,或刚Flush Buffer_cache时,所有块会被放在辅助LRU中。
6、前台进程(服务器进程)扫描主、辅LRU时,会将遇到的TCH为2以下的脏块,移到主LRUW。
7、DBWR三秒一次,扫描检查点队列的长度、确定是否要写脏块。另外,DBWR每三秒还会扫描主LRUW,将其中的脏块移到辅助LRUW,然后写到磁
盘。
8、从检查点队列写到磁盘中的块,不会改变它在LRU链表中的位置。从LRUW写到磁盘中的块,会被放于LRUW,以供马上覆盖。
基本上就这些了。下面开始吧。本篇比较简单,没有DTrace,更进一步的DTrace测试,放在下篇中吧。只是实验较多,繁琐了些。


第 一 章       一个测试理解什么是主、辅LRU

LRU是Buffer Cache池中的重要链表,它的作用我不再详述,已经有很多相关资料。这次主要和大家讨论下主LRU、辅助LRU的作用。
先来看一个测试。
步1:环境介绍
先来看看Buffer Cache的大小:
SQL> show sga
Total System Global Area 1073741824 bytes
Fixed Size                  1284344 bytes
Variable Size             960497416 bytes
Database Buffers          104857600 bytes
Redo Buffers                7102464 bytes
Buffer Cache大小100M。
再来看看测试表大小:
SQL> set linesize 1000
SQL> col segment_name for a30
SQL> SQL> select segment_name,bytes/1024/1024 from dba_segments where segment_name='A3_70M';
SEGMENT_NAME                   BYTES/1024/1024
------------------------------ ---------------
A3_70M                                      80
SQL> SQL> select segment_name,bytes/1024/1024 from dba_segments where segment_name='A4_70M';
SEGMENT_NAME                   BYTES/1024/1024
------------------------------ ---------------
A4_70M                                      80
两张测试表,A3_70M、A4_70M,各自大小80M。本来是想把它们两个大小建为70M的,所以名字带有后缀_70M,但建的大了一点,不过,这无所
谓,不会影响我们的测试结果。
步2:刷新Buffer Cache池,观察LRU链长度
SQL> alter system flush buffer_cache;
SQL> select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE  from x$kcbwds;
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
6238       6238       6219          0          0
6237       6237       6224          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
0          0          0          0          0
16 rows selected.
这个视图返回16行,但只有两行有数据。其他行都是0,原因是什么?
查一下select name from v$latch_children where name ='cache buffers lru chain'; 就可以知道,我一共有16个cache buffers lru 
chain Latch,但只有两个被使用了。
每一个cache buffers lru chain latch,都算作一个“工作组”,work set。意义是指每个cache buffers lru chain latch,都对应一组主
、辅LRU链,和脏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。我们先只关注主、辅LRU。
来看我们的显示结果吧,两个工作组加起来,一共12475个块。
另外,可以看到,CNUM_REPL的ANUM_REPL列的几乎相等,这说明绝大多数块,都在辅助LRU链表中,这是为什么呢?这是我们今天第一点结论,
flush了buffer cache、或刚刚开启数据库时,所有的可用Buffer都会链接到辅助LRU中。
步3:先查询a3_70M,再查询a4_70M,分别观察物理读。(注意顺序,先查询A3_70M,再查询A4_70M)
SQL> set autot trace
SQL> select count(*) from a3_70m;
Execution Plan
----------------------------------------------------------
Plan hash value: 850873958
---------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Cost (%CPU)| Time     |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |     1 |  1643   (1)| 00:00:20 |
|   1 |  SORT AGGREGATE    |        |     1 |            |          |
|   2 |   TABLE ACCESS FULL| A3_70M |  1511K|  1643   (1)| 00:00:20 |
---------------------------------------------------------------------
Statistics
----------------------------------------------------------
207  recursive calls
0  db block gets
9292  consistent gets
9271  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
4  sorts (memory)
0  sorts (disk)
1  rows processed
SQL> select count(*) from a4_70m;
Execution Plan
----------------------------------------------------------
Plan hash value: 2047399966
---------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Cost (%CPU)| Time     |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |     1 |  1643   (1)| 00:00:20 |
|   1 |  SORT AGGREGATE    |        |     1 |            |          |
|   2 |   TABLE ACCESS FULL| A4_70M |  1502K|  1643   (1)| 00:00:20 |
---------------------------------------------------------------------
Statistics
----------------------------------------------------------
207  recursive calls
0  db block gets
9292  consistent gets
9259  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
4  sorts (memory)
0  sorts (disk)
1  rows processed
可以看到,第一次查询A3_70M和A4_70M,物理读分别是9200多。
步4:再次查询a3_70M,再查询a4_70M,分别观察物理读。(顺序无所谓,先查谁都可以)
SQL> select count(*) from a3_70m;
Execution Plan
----------------------------------------------------------
Plan hash value: 850873958
---------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Cost (%CPU)| Time     |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |     1 |  1643   (1)| 00:00:20 |
|   1 |  SORT AGGREGATE    |        |     1 |            |          |
|   2 |   TABLE ACCESS FULL| A3_70M |  1511K|  1643   (1)| 00:00:20 |
---------------------------------------------------------------------
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
213  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
SQL> select count(*) from a4_70m;
Execution Plan
----------------------------------------------------------
Plan hash value: 2047399966
---------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Cost (%CPU)| Time     |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |     1 |  1643   (1)| 00:00:20 |
|   1 |  SORT AGGREGATE    |        |     1 |            |          |
|   2 |   TABLE ACCESS FULL| A4_70M |  1502K|  1643   (1)| 00:00:20 |
---------------------------------------------------------------------
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
9135  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
发现没有,A3_70M的物理读在第二次查询时大量下降,只有213次。而A4_70M在第二次查询时,物理读下降很不明显,还是9200次左右。
可以再测个几遍,情况依旧。A3_70M的物理读很少,而A4_70M的物理读很多。
这是我们今天的问题一:考虑这是为什么。
下面继续我们的问题2。
步5:连续两次查询A3_70M和A4_70M
SQL> select count(*) from a4_70m;
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
8686  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
SQL> select count(*) from a4_70m;
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
8722  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
这是两次A4_70M的结果对比,多次查询A4_70M之后,物理读下降到了8700多,然后不再下降。
再来看两次连续查询A3_70M的结果对比:
SQL> select count(*) from a3_70m;
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
600  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
SQL> select count(*) from a3_70m;
Statistics
----------------------------------------------------------
0  recursive calls
0  db block gets
9269  consistent gets
0  physical reads
0  redo size
414  bytes sent via SQL*Net to client
381  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed
第一次有600次物理读,第二次就没有了。
问题2出来了,考虑这是没什么。
步6:暂时总结
1、A3_70M和A4_70M表大小一样。行数一样、所占空间也一样,都是80M
2、第一次查询时,我们是先查询A3_70M,再查A4_70M。如果你反过来,先查A4_70M,将看到A4_70M的物理读在第二次查询时大大减少,连续两
次查询时物理读为0。
两个问题:
1、为什么第一次查询时,先查的表,在第二次查询时物理读会大大减少,而后查的表则不会。
2、为什么第一次查询时,先查的表连续两次查询时物理读会减少为0,而后查的表不会。
一个提示:可以观察一下我们开始提到的x$kcbwds视图。
步7:如何观察块处在哪个链上。
如何进一步分析这个问题?其实这两个问题的回答,都需要着落到一件事上,就是要分析两个表对应的块,分别在什么链上。是在主LRU,还是
辅LRU。如何进行这个观察呢?其实很简单,x$bh中有个LRU_FLAG列,通过这个列,就可以确定块在哪个链表上。
我们来看一下这个视图:
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有4种值,6、2、8和0。这4个值分别代别什么意义呢?确定这个其实很简单,DUMP一下块对应的Buffer
就行了,以LRU_FLAG为6的为例:
(1)、随便查找LRU_FLAG为6的一行:
SQL> select lru_flag,file#,dbablk,TS# from x$bh where LRU_FLAG=6 and rownum=1;
LRU_FLAG      FILE#     DBABLK        TS#
---------- ---------- ---------- ----------
6          5      12459          4
(2)、如下DUMP一下这个BUffer:
SQL> alter session set events 'immediate trace name SET_TSN_P1 level 5';
Session altered.
SQL> alter session set events 'immediate trace name BUFFER level 0x014030ab';
Session altered.
上面这两个命令,“SET_TSN_P1 level 5”中的5,是表空间号加1得到的。也就是TS#+1。
第二个命令中“BUFFER level 0x014030ab”,其中0x014030ab,是5号文件12459号块的DBA。
这个DBA是如何计算出的?其实我是在另一个会话中DUMP一下5号文件12459号块,查看DUMP结果才得到的,Oracle也提供了一个包,其实有个函
数可以根据文件号、块号生成DBA,包名、函数名我都忘了,只好用笨方法。
好,来查看DUMP结果吧,到user_dump_dest目录中,找到日期最靠后的(我一般是ls -lFrt),它就是我们刚刚生成的DUMP文件。在它的开头,
我们可以看到如下内容:
*** 2012-07-03 08:58:36.571
*** SERVICE NAMESYS$USERS) 2012-07-03 08:58:36.523
*** SESSION ID874.3) 2012-07-03 08:58:36.523
Dump of buffer cache at level 10 for tsn=4, rdba=20983979
BH (7bbf0764) file#: 5 rdba: 0x014030ab (5/12459) class: 1 ba: 7b9dc000
set: 5 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: 9738 objn: 9738 tsn: 4 afn: 5
hash: [7d7e687c,8e94b67c] lru: [7bbe80b8,7bbf0704]
lru-flags: moved_to_tail on_auxiliary_list
ckptq: [NULL] fileq: [NULL] objq: [7bbf075c,7bbe8110]
st: XCURRENT md: NULL tch: 0
flags: only_sequential_access
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
buffer tsn: 4 rdba: 0x014030ab (5/12459)
scn: 0x0000.00418edb seq: 0x01 flg: 0x06 tail: 0x8edb0601
frmt: 0x02 chkval: 0xc400 type: 0x06=trans data
我对这里面的东西,不过多解释了,其实有一行:
lru-flags: moved_to_tail on_auxiliary_list
这一行说明了块对应Buffer所在LRU链表的状态。Buffer目前在辅助LUR,moved_to_tail说明Buffer正移向链表末尾。
好了,我们已经总结出来,LRU_FLAG列为6,说明Buffer在辅助LRU链表。
其他的我不再一一列出测试过程了,总结一下,LRU_FLAG为6、4,说明BUffer在辅助LRU中。为0、2,说明在主LRU的冷端,为8、9的,说明在
主LRU的热端。
步8:问题1:为什么第一次查询时,先查的表,在第二次查询时物理读会大大减少,而后查的表则不会
让我们来观察一下A3_70M、A4_70M 的块都在什么链上
SQL> select lru_flag,count(*) from x$bh a,dba_objects b where a.obj=b.data_object_id and object_name='A3_70M' group by lru_flag;
LRU_FLAG   COUNT(*)
---------- ----------
2       8554
0          1
SQL> select lru_flag,count(*) from x$bh a,dba_objects b where a.obj=b.data_object_id and object_name='A4_70M' group by lru_flag;
LRU_FLAG   COUNT(*)
---------- ----------
6       3082
2        366
0          1 
这两条语句,我就不再过多解释了,根据LRU_FLAG列的值,我们可以确认,A3_70M,共有8555个块在Buffer Cache中,所有相关的块都在主LRU
链。A4_70M,只有3400多个块在Buffer Cache中,而且大部分块都在辅助LRU。
为什么A3_70M的块,被读进Buffer Cache时,大多被放置在主LRU的冷端?而A4_70M的块,则被放进辅助LRU呢?
能回答这个问题,我们今天的问题也就有了答案。
步9:回答问题1
有如下三点基础知识:
1、当数据库刚刚打开、或刚刚做过FLush Buffer_cache操作时,所有的块,都被放进辅助LRU。
2、进程寻找可用块时,先会在辅助LRU中查找,然后才会到主LRU中找。
3、Oracle会保持20%至25%左右的块在辅助LRU链,80%的在主LRU。这一点,从观察x$kcbwds中的CNUM_REPL、ANUM_REPL列,可以很容易的验证

有了这三点基础知识,让我们来得出结论吧:
当所有块都在辅助LRU时,Oracle为了急于保证主、辅LRU,块数量80%,20%的比例,第一次查询时,会将大量的块移到主LRU。也就是说,第一
次查询A3_70M时,大至步骤是这样的,分两种情况:
一、主LRU几乎为空时:
1、在辅助LRU找到可用块。
2、将它移到主LUR
3、将数据读入此块。
二、当主、辅LRU的块数量比例到达80%、20%后
1、在辅助LRU找到查用块
2、所处链表不变,直接将数据读入此块。
3、物理读完成后,块还在辅助LRU中。
由于第一次查询A3_70M时,主LRU几乎为空,所以,它的绝大部分的块被放在了主LRU,少部分块被放入了辅助LRU。
根据比例,辅助LRU中将会有3000到3200个左右的块。根据我们的测试结果,全表扫描A4_70M后,有3000多个块在辅助链上。根据这个,得出我
们今天第二个结论,正常的全表扫描操作,将只会反复使用辅助LRU中的块。
当然,我们的测试中,还是有少量A4_70M的块进入到了主LRU,不过数量不多,只有几百个。
好了,回答一开始的问题吧,第一次查询A3_70M,Oracle会了保持主、辅LRU的比例,将很多被A3_70M占用的块移进了主LRU,所以,A3_70M在
Buffer Cache中可以有很多块,再次查询A3_70M时,物理读大大减少。
而全表扫描A4_70M,只能反复使用辅LRU中的块,几乎无法占用主LRU中的块,因此在Buffer Cache中块数较少,每次物理读都很高。
步10:回答问题2
为什么连续两次查询A3_70M,就没有物理读了?可以连续两次全表扫描一下A3_70M,查看结果:
SQL> select lru_flag,count(*) from x$bh a,dba_objects b where a.obj=b.data_object_id and object_name='A3_70M' group by lru_flag;
LRU_FLAG   COUNT(*)
---------- ----------
6       1004
2       8254
0          1
一共9000多个块,8000多个在主LRU,1000多一点在辅助LRU。A3_70M表的全部块,都可以被缓存到Buffer Cache中。
当再次查询A4_70M时,A4_70M将又反复占用辅助LRU中的块,A3_70M留在辅助LRU中的1000多一点块会被覆盖,所以,当A3_70M、A4_70M交替查
询时,每次A3_70M在辅助LRU中的块都会被覆盖,因此,它会有少量物理读,但,当连续查询A3_70M时,辅助LRU中的块没被覆盖,就不会有物理读了。
好,问题已经解答了,让我们总结一下吧。
总结:
1、flush了buffer cache、或刚刚开启数据库时,所有的可用块都会链接到辅助LRU中。
2、查找可用块的过程,是先找辅助LRU,再从主LRU的冷端尾开始找。
3、全表扫描在辅助LRU找到块后,不会将块读进主LRU。所以,全表扫描的结果很容易被覆盖。
4、非全表扫描时,在辅助LRU找到块后,会将块移到主LRU的冷端头。这一点我们还没验证过。
5、全表扫描的块,如果再次以非全表扫描方式访问,TCH列会增加,但会一直留在辅助LRU,不会被移到主LRU冷端头。只有等到TCH值超过2时,才会在
下次被扫描到时,移到主LRU热端。
第4点和第5点,我们并没用测试验证,但找个这样的测试也是很简单的。留给大家自己验证一下吧,研究Internal,结果不是目的,过程更有
意义。
最后还有一个注意事项,这个测试只能在10G下做,因为11GR2后有些改变,即使是开库后第一次全表扫描,也不会把块移往主LRU,这样其实更合理,Oracle其实一直没有停止对内核优化步伐。
另外,本篇中除这个测试外,其他测试都可以在10G、11GR2下做。


第 二 章       LRUW

脏块什么时候会进入LRUW ? 流行的说法有以下几种:
1、DBWR 每3秒醒来时,会将脏块收集到LRUW,当LRUW中脏块达到一定阀值时,将触发DBWR写LRUW中的脏块。
2、当前台进程(也就是服务器进程)扫描LRU时,会将发现的脏块移到LRUW中,当LRUW中脏块达到一定阀值时,将触发DBWR写LRUW中的脏块。
3、当前台进程扫描LRU的一定数量的块后,都没有发现可用块,此时会触发紧急写。前台进程等待,DBWR将脏块从检查点队列移到LRUW,从
LRUW写到磁盘。
以上这三种说法,第一种并不存在,或者说,都观察不到。
通过x$kcbwds视图的CNUM_WRITE、ANUM_WRITE列,就可以观察到主、辅LRUW链的长度。可以一直不停的多观察几十秒,没有发现这两个列不为0
。3秒的说法,不功自破。
但是,并不能说3秒写磁盘的操作不存在,只是,并不是以这种形式。
第二种说法,流传很广,也的确会有这种情况出现,但真实情况和流行说法还是会有不同。
具体怎么来验证一下呢。先来做个测试:
步1:环境配置:
写脏块有两种途径:通过检查点队列、通过LRUW。为了避免从检查点队列写影响我们的测试结果,我们要将增量检查点关闭。
其实,不是关闭,而是将增量检查点周期设的很大,在我们的测试期间,保证它不会触发。
需要设置如下几个参数:
SQL> show parameter log_checkpoint_timeout
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_checkpoint_timeout               integer     100000
SQL> show parameter mttr
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fast_start_mttr_target               integer     0
将log_checkpoint_timeout设置为了10万秒。fast_start_mttr_target设置为0。
另外,10G之后,fast_start_mttr_target设置为0时,Oracle将打开一个Self Tune Checkpoint(自调节检查点)的功能,将它也关闭:
SQL> alter system set "_disable_selftune_checkpointing"=true;
System altered.
经过这样的设置,增量检查点虽然没有关闭,也和关闭差不多了,10万秒才会被触发。
当然,日志切换时还会触发一个“日志切换时的增量检查点”,虽和普通的增量检查点有不同,但大体上类似增量检查点。这个只要建个稍大
点的日志文件,就可以解决,比如,我的日志文件是500M。
步2:观察当前系统中的脏块
SQL> select count(*) from v$bh where dirty='Y';
COUNT(*)
----------
46
步2:在另一个会话中,扫描个非全表扫描
select /*+index(a2_70m)*/ * from a2_70m where id1<=100000000;
索引范围扫描,将先找辅助LRU、再找主LRU。
步3:在步2中的索引范围扫描执行期间,不停的显示:
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时,跳过的脏块数。我们可以观察到如下结果:
…………………………………………省略一部分相同的结果……………………………………………………
SQL> select * from v$sysstat where name in ('dirty buffers inspected');
STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
---------- ---------------------------------------------------------------- ---------- ---------- ----------
94 dirty buffers inspected                                                   8        650 1344569897
SQL> 
SQL> select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE  from x$kcbwds where cnum_set>0;
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
6238       6238       1522          0          0
6237       6237       1541          0          0
SQL> select * from v$sysstat where name in ('dirty buffers inspected');
STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
---------- ---------------------------------------------------------------- ---------- ---------- ----------
94 dirty buffers inspected                                                   8        650 1344569897
SQL> 
SQL> select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE  from x$kcbwds where cnum_set>0;
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
6238       6237       1523          1          0
6237       6235       1541          2          0
SQL> select * from v$sysstat where name in ('dirty buffers inspected');
STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
---------- ---------------------------------------------------------------- ---------- ---------- ----------
94 dirty buffers inspected                                                   8        653 1344569897
SQL> 
SQL> select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE  from x$kcbwds where cnum_set>0;
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
6238       6237       1520          1          0
6237       6235       1539          2          0
SQL> select * from v$sysstat where name in ('dirty buffers inspected');
STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
---------- ---------------------------------------------------------------- ---------- ---------- ----------
94 dirty buffers inspected                                                   8        653 1344569897
…………………………………………省略一部分相同的结果……………………………………………………
SQL> 
SQL> select CNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE ,ANUM_WRITE  from x$kcbwds where cnum_set>0;
CNUM_SET  CNUM_REPL  ANUM_REPL CNUM_WRITE ANUM_WRITE
---------- ---------- ---------- ---------- ----------
6238       6238       1507          0          0
6237       6237       1527          0          0
SQL> select * from v$sysstat where name in ('dirty buffers inspected');
STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
---------- ---------------------------------------------------------------- ---------- ---------- ----------
94 dirty buffers inspected                                                   8        653 1344569897
…………………………………………省略一部分相同的结果……………………………………………………
前面的观察结果,dirty buffers inspected的值一开始为650,当变为653时,同时,可以在CNUM_WRITE列看到,此列的值增加了3。
这证明有三个脏块被跳过,移到了LRUW上。但是,不同于通常的说法,没有等LRUW中脏块数达到什么阀值,很快的,这三个脏块就被写的磁盘
了。

3个块脏块,绝对没有到达任何阀值。

时间: 2024-08-30 07:38:10

揭密Oracle之七种武器之四:揭密Buffer Cache中的链表的相关文章

buffer cache中的LRU链+

转自晶晶小妹的博客:http://space.itpub.net/13095417/viewspace-204007 一.LRU链介绍HASH是快速查找时,常用的算法.Oracle中几乎在所有需要快速查找的地方,都使用了HASH算法.LRU则是在"资源重用"时,常用的算法.       在Buffer cache中,LRU链被分为两半,分别是热端.冷端,在默认方式下,热.冷端各占50%的块,这点可以由隐藏参数_db_percent_hot_default控制.当块第一次被读进Buffe

如何转储数据文件和Buffer Cache中的数据块

这篇文章是为了补充<Oracle性能优化与诊断案例精选>一书中的案例而写的,但是想想,也许还可以扩展一下,对于刚接触 Oracle 数据库的朋友们,试着回答一下以下几个问题,看看自己能否找到正确的答案: 当我们 insert 一条记录,不提交,这个数据在内存还是磁盘? 当我们 insert 一条记录,提交,这个数据在内存还是磁盘? 当我们 insert 一条记录,不提交,检查点,这个数据在内存还是磁盘? 当我们 insert 一条记录,提交,检查点,这个数据在内存还是磁盘? 先看看我的测试:

如何使用events DUMP buffer cache中指定的数据块

介绍了DUMP  buffer cache中全部数据块及指定RDBA的数据块方法. 1.DUMP  buffer cache中全部数据块 ALTER SESSION SET EVENTS 'immediate trace name buffers level n'; 1 buffer header 2 level 1 + block header 3 level 2 + block contents 4 level 1 + hash chain 5 level 2 + hash chain 6

oracle Buffer Cache的管理机制

1.LRU链表结构概述 在前面,我们已经知道了Oracle是如何在hash chain中搜索要找的数据块所对应的buffer header的过程,我们也知道如果在hash chain上没有找到所要的buffer header时,Oracle会发出I/O调用,到磁盘上的数据文件中获取数据块,并将该数据块的内容复制一份到buffer cache中的内存数据块里.这个时候,假如buffer cache是空的,比较好办,直接拿一个空的内存数据块来用即可.但是如果buffer cache中的内存数据块全都

oracle中如何设置buffer cache

buffer cache的设置随着Oracle版本的升级而不断变化.Oracle 8i下使用db_block_buffers来设置,该参数表示buffer cache中所能够包含的内存数据块的个数:Oracle 9i以后使用db_cache_size来设置,该参数表示buffer cache的总共的容量,可以用字节.KB.MB为单位来进行设置.而到了Oracle 10g以后则更加简单,甚至可以不用去单独设置buffer cache的大小.因为Oracle 10g引入了ASMM(Automatic

Oracle内存结构详解(二) Oracle管理Buffer Cache

Buffer Cache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域.Oracle进程如果发现需要访问的数据块已经在buffer cache中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能(内存的读取效率是磁盘读取效率的14000倍).Buffer cache对于所有oracle进程都是共享的,即能被所有oracle进程访问. 和Shared Pool一样,buffer cache被分为多个集合,这样能够大大降低多CPU系统中的争用问题. 1.Buffer c

《Oracle高性能自动化运维》一一2.4 Buffer Cache

2.4 Buffer Cache 2.4.1 Buffer(Cache)Pool 1. Buffer(Cache)Pool组成结构 Oracle Buffer Cache由3个Buffer Pool组成,如图2-9所示. 其中: Default Pool:默认池,用于缓存常规数据: Keep Pool:保留池,主要用于缓存频繁更新的小表: Recycle Pool:回收池,用于缓存随机使用的大表. 2. Buffer Pool与Working Set Buffer Pool与Working Se

Buffer cache 的调整与优化(二)

--******************************** -- Buffer cache 的调整与优化(二) --********************************         Buffer cache 实际上细分为多个不同的Buffer cache,如keep pool,recycle pool,default pool,下面描述不同buffer cache的使用.     有关Buffer cache 的总体描述,请参考:Buffer cache 的调整与优化(

Buffer cache 的调整与优化(一)

--============================== -- Buffer cache 的调整与优化(一) --==============================       Buffer Cache是SGA的重要组成部分,主要用于缓存数据块,其大小也直接影响系统的性能.当Buffer Cache过小的时候,将会造成更多的 free buffer waits事件. 下面将具体描述Buffer Cache的作用,调整与优化.   一.SGA的所有组件     从动态视图v$sg