首页 > 代码库 > buffer pool 和shared pool 详解(一)

buffer pool 和shared pool 详解(一)

【深入解析--eygle】学习笔记


1.1 buffer pool原理

Buffer Cache是Oracle SGA中一个重要部分,通常的数据访问和修改都需要通过BufferCache来完成。当一个进程需要访问数据时,首先需要确定数据在内存中是否存在,如果数据在Buffer中存在,则需要根据数据的状态来判断是否可以直接访问还是需要构造一致性读取;如果数据在Buffer中不存在,则需要在Buffer Cache中寻找足够的空间以装载需要的数据,如果Buffer  Cache中找不到足够的内存空间,则需要触发DBWR去写出脏数据,释放Buffer空间。

1.1.1 LRU 与LRUW List

在Buffer Cache中,Oracle通过几个链表进行内存管理,其中最为熟知的是LRU List和LRUW List(也经常被称为Write/Dirty List),各种List上存放的是指向具体的Buffer的指针等信息。

从Oracle8开始,为了实施增量检查点,Oracle还引入了检查点队列- Checkpoint Queue和文件队列  –  File Queue;从Oracle8i开始,由于异步DBWn的引入,现在关于各种List以及Queue的更为精确的概念是工作集(WS - Working Sets), 在 每 个WS中包含几个不同功能的List,每 个List都通过Cache Buffers LRU CHAIN Latch进行保护,当使 用 多 个DBWR进程时(通过DB_WRITER_PROCESSES参数可以设置数据库使用多个DBWR进程),数据库中会存在多个WS,同时当使用Buffer Cache的多缓冲池技术时,每个独立的缓冲池也会存在各自独立的WS。

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

下图是Buffer Cache中LRU及LRUW List的简要示意图:

 

 

检查点队列(Checkpoint Queue)则负责按照数据块的修改顺序记录数据块,同时将RBA和数据块关联起来,这样在进行增量检查点时,数据库可以按照数据块修改的先后顺序将其写出,从而在进行恢复时,可以根据最后写出数据块及其相关的RBA开始进行快速恢复。在检查点触发时DBWR根据检查点队列执行写出,在其他条件触发时,DBWR由Dirty List执行写出。检查点队列的内存在Shared Pool内存中分配:

 

同样具有相关Latch对其进行保护:

15:48:38 sys@felix SQL>select name ,gets,missesfrom v$latch where name like ‘%checkpoint queue%‘;

 

NAME                                          GETS     MISSES

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

active checkpoint queue latch                 10247          0

checkpoint queue latch                       145659          1

 

15:48:39 sys@felix SQL>

 

 

可以通过下图来详细介绍一下BufferCache的原理及使用:

 


1.当一个Server进程需要读数据到Buffer Cache中时,首先必须判断该数据在Buffer

中是否存在(图中①所示过程),如 果 存 在 且 可 用 ,则获取该数据,同时根据LRU

算法增进其访问计数;如果Buffer中不存在该数据,则需要从数据文件上进行读取。

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

3. 如果LRUW  Queue超过了阀值,Server进程就会通知DBWn去写出脏数据(图中③所示过程);

 

这也是触发DBWn写的一个条件,这个阀值曾经提到是25%,也就是当Dirty Queue超过25%满就会触发DBWn的写操作:

 

16:13:58 sys@felix SQL>select kvittag, kvitval,kvitdsc from x$kvit where kvittag =‘kcbldq‘;

 

KVITTAG                 KVITVAL KVITDSC

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

kcbldq                       25 large dirty queue ifkcbclw reaches this

 

16:14:04 sys@felix SQL>

 

如果Server进程扫描LRU超过一个阀值仍然不能找到足够的Free Buffer,将停止寻找,转而通知DBWn去写出脏数据,释放内存空间。

 

同样这个阀值可以从以上字典表中查询得到,这个数字是40%,也就是说当Server进程扫描LRU超过40%还没能找到足够的Free  Buffer就会停止搜索,通知DBWn执行写出,这是进程会处于free buffer wait等待

 

 

16:18:03 sys@felix SQL>col  KVITDSC for a60

16:18:20 sys@felix SQL>select kvittag, kvitval,kvitdsc from x$kvit where kvittag = ‘kcbfsp‘;

 

KVITTAG                 KVITVAL KVITDSC

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

kcbfsp                       40 Max percentage of LRUlist foreground can scan for free

 

16:18:24 sys@felix SQL>

 

 

同时我们知道,由于增量检查点的引入,DBWn也会主动扫描LRU  List,将发现的Dirty  Buffer注册到Dirty  List以及Checkpoint  Queue,这个扫描也受一个内部约束,在Oracle9iR2中,这个比例是25%:

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

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

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

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

 

 

alter session set events ‘immediate trace name buffers level4‘;

 

16:33:14 sys@felix SQL>select value from v$diag_info;

 

VALUE

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

/u01/app/oracle/diag/rdbms/felix/felix/trace/felix_ora_7187.trc

 

 

 

不同level转储的内容详细程度不同,此命令的可用级别主要有1~10级,其中各级别的含义如下。

(1)  Level 1:仅包含BufferHeaders信息。

(2)  Level 2:包含BufferHeaders和Buffer概要信息转储。

(3)  Level 3:包含BufferHeaders和完整Buffer内容转储。

(4)  Level 4:Level 1 +Latch转储  + LRU队列。

(5)  Level 5:Level 4 +Buffer概要信息转储。

(6)  Level 6和Level 7:Level 4 + 完整的Buffer内容转储。

(7)  Level 8:Level 4 + 显示users/waiters信息。

(8)  Level 9:Level 5 + 显示users/waiters信息。

(9)  Level 10:Level 6 + 显示users/waiters信息

 

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

 

16:33:27 sys@felix SQL>show parameter max_dump

 

NAME                                 TYPE                   VALUE

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

max_dump_file_size                   string                 unlimited

16:47:37 sys@felix SQL>show parametermemory_target

 

NAME                                 TYPE                   VALUE

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

memory_target                        big integer            400M

16:48:46 sys@felix SQL>

 

 

 

查看文件 /u01/app/oracle/diag/rdbms/felix/felix/trace/felix_ora_7187.trc信息:

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

 

*** 2014-07-22 16:33:13.185

*** SESSION ID:(41.237) 2014-07-22 16:33:13.185

*** CLIENT ID:() 2014-07-22 16:33:13.185

*** SERVICE NAME:(SYS$USERS) 2014-07-2216:33:13.185

*** MODULE NAME:(sqlplus@felix (TNS V1-V3))2014-07-22 16:33:13.185

*** ACTION NAME:() 2014-07-22 16:33:13.185

 

Dump of buffer cache at level 4 for tsn=2147483647rdba=0

  (WS) size: 0 (0) wsid: 1 state: 0 pool: 1

   (WS_REPL_LIST) main_prev: 0x774b2f18 main_next: 0x774b2f18 aux_prev:0x774b2f28 aux_next: 0x774b2f28

    curnum:0 auxnum: 0

    cold:774b2f18 hbmax: 0 hbufs: 0

   (WS_WRITE_LIST) main_prev: 0x774b2f48 main_next: 0x774b2f48 aux_prev:0x774b2f58 aux_next: 0x774b2f58

    curnum:0 auxnum: 0

   (WS_XOBJ_LIST) main_prev: 0x774b2f78 main_next: 0x774b2f78 aux_prev:0x774b2f88 aux_next: 0x774b2f88

    curnum:0 auxnum: 0

   (WS_XRNG_LIST) main_prev: 0x774b2fa8 main_next: 0x774b2fa8 aux_prev:0x774b2fb8 aux_next: 0x774b2fb8

    curnum:0 auxnum: 0

   (WS_REQ_LIST) main_prev: 0x774b2fd8 main_next: 0x774b2fd8 aux_prev:0x774b2fe8 aux_next: 0x774b2fe8

    curnum:0 auxnum: 0

   (WS_L2WRT_LIST) main_prev: 0x774b3008 main_next: 0x774b3008 aux_prev:0x774b3018 aux_next: 0x774b3018

    curnum:0 auxnum: 0

   (WS_L2REPL_LIST) main_prev: 0x774b3038 main_next: 0x774b3038 aux_prev:0x774b3048 aux_next: 0x774b3048

    curnum:0 auxnum: 0

   (WS_L2KEEP_LIST) main_prev: 0x774b3068 main_next: 0x774b3068 aux_prev:0x774b3078 aux_next: 0x774b3078

    curnum:0 auxnum: 0

  (WS) fbwanted: 0

  (WS) bgotten: 0 sumwrt: 0

  (WS) pwbcnt: 0, last: 0

MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]

 

接下来是具体的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]

MAIN L2W_LST Queue header (NEXT_DIRECTION)[NULL]

MAIN L2W_LST Queue header(PREV_DIRECTION)[NULL]

AUXILIARY L2W_LST Queue header(NEXT_DIRECTION)[NULL]

AUXILIARY L2W_LST Queue header(PREV_DIRECTION)[NULL]

MAIN L2R_LST Queue header (NEXT_DIRECTION)[NULL]

MAIN L2K_LST Queue header (NEXT_DIRECTION)[NULL]

MAIN L2R_LST Queue header (PREV_DIRECTION)[NULL]

AUXILIARY L2R_LST Queue header(NEXT_DIRECTION)[NULL]

AUXILIARY L2K_LST Queue header(NEXT_DIRECTION)[NULL]

MAIN L2K_LST Queue header (PREV_DIRECTION)[NULL]

AUXILIARY L2K_LST Queue header(NEXT_DIRECTION)[NULL]

AUXILIARY L2K_LST Queue header(PREV_DIRECTION)[NULL]

  (WS) size:0 (0) wsid: 2 state: 0 pool: 2

   (WS_REPL_LIST) main_prev: 0x774ce4b0 main_next: 0x774ce4b0 aux_prev:0x774ce4c0 aux_next: 0x774ce4c0

    curnum:0 auxnum: 0

    cold:774ce4b0 hbmax: 0 hbufs: 0

   (WS_WRITE_LIST) main_prev: 0x774ce4e0 main_next: 0x774ce4e0 aux_prev:0x774ce4f0 aux_next: 0x774ce4f0

    curnum:0 auxnum: 0

   (WS_XOBJ_LIST) main_prev: 0x774ce510 main_next: 0x774ce510 aux_prev:0x774ce520 aux_next: 0x774ce520

    curnum:0 auxnum: 0

   (WS_XRNG_LIST) main_prev: 0x774ce540 main_next: 0x774ce540 aux_prev:0x774ce550 aux_next: 0x774ce550

    curnum:0 auxnum: 0

   (WS_REQ_LIST) main_prev: 0x774ce570 main_next: 0x774ce570 aux_prev:0x774ce580 aux_next: 0x774ce580

    curnum:0 auxnum: 0

   (WS_L2WRT_LIST) main_prev: 0x774ce5a0 main_next: 0x774ce5a0 aux_prev:0x774ce5b0 aux_next: 0x774ce5b0

    curnum:0 auxnum: 0

   (WS_L2REPL_LIST) main_prev: 0x774ce5d0 main_next: 0x774ce5d0 aux_prev:0x774ce5e0 aux_next: 0x774ce5e0

    curnum:0 auxnum: 0

   (WS_L2KEEP_LIST) main_prev: 0x774ce600 main_next: 0x774ce600 aux_prev:0x774ce610 aux_next: 0x774ce610

    curnum:0 auxnum: 0

  (WS)fbwanted: 0

  (WS)bgotten: 0 sumwrt: 0

  (WS)pwbcnt: 0, last: 0

MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]

MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]

AUXILIARY RPL_LST Queue header (NEXT_DIRECTION)[NULL]

 

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

 

 

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

 

  (WS)bgotten: 17019 sumwrt: 6954

  (WS)pwbcnt: 0, last: 25

MAIN RPL_LST Queue header(NEXT_DIRECTION)[0x6a7e6170,0x6bbe17d0]

0x6a7e6088=>0x6a7e61b8=>0x6a7e62e8=>0x6a7e6418=>0x6a7e6548=>0x6a7e6678=>0x6a7e67a8=>0x6a7e68d8

0x6a7e6a08=>0x6b3e8c78=>0x6bfd8608=>0x6cbf9c68=>0x6bbf4358=>0x69bdf5a8=>0x6cbd8278=>0x6abfc268

0x6abfc398=>0x6abfc4c8=>0x6abfc5f8=>0x6afd8018=>0x6afd8148=>0x6afd8278=>0x6afd83a8=>0x6afd84d8

0x6afd8738=>0x6afd8868=>0x6afd8f88=>0x6afd8e58=>0x6afd8998=>0x6b7f38a8=>0x6afdc038=>0x6afdc168

0x6afdc4f8=>0x6afdc628=>0x6afdc758=>0x6afdc888=>0x6afdd208=>0x6afdd338=>0x6afdd468=>0x6afdd6c8

0x6afdd7f8=>0x6afdd928=>0x6afdda58=>0x6afddb88=>0x6afddcb8=>0x6afddde8=>0x6afddf18=>0x6afde048

0x6afde178=>0x6afde2a8=>0x6afde3d8=>0x6afde508=>0x6afde898=>0x6afde9c8=>0x6afdeaf8=>0x6afdec28

0x6afded58=>0x6afdf0e8=>0x6afdf218=>0x6afdf478=>0x6afdf5a8=>0x6afdf6d8=>0x6afdfb98=>0x6afe0e98

0x6afe36f8=>0x6afe54a8=>0x6afe55d8=>0x6afe5708=>0x6afe5968=>0x6afe5bc8=>0x6afe5cf8=>0x6afe5e28

0x6afe62e8=>0x6afe6418=>0x6b7f2478=>0x6afe6a08=>0x6afe6b38=>0x6afe6c68=>0x6afe6d98=>0x6afe6ec8

0x6afe6ff8=>0x6afe7128=>0x6afe7258=>0x6afe7f68=>0x6afe8b48=>0x6afe8c78=>0x6afe9988=>0x6afe9ab8

0x6afe9be8=>0x6afe9f78=>0x6afea0a8=>0x6afea1d8=>0x6afea8f8=>0x6afeb3a8=>0x6afeb998=>0x6afebac8

0x6afebbf8=>0x6afebf88=>0x6afec0b8=>0x6afec1e8=>0x6afec318=>0x6afec578=>0x6afec6a8=>0x6afec7d8

0x6afec908=>0x6afeca38=>0x6afecdc8=>0x6afecef8=>0x6afed028=>0x6afed158=>0x6afed288=>0x6afed3b8