在BUFFER CACHE中,oracle通过几个链表进行内存管理,最为人熟知的是LRU LIST与DIRTY LIST(WRITE LIST或CHECKPOINT QUENE),各个LIST上存放的是指向具体BUFFER的指针。
注:除LRU和DIRTY LIST这两个重要链表之外,BUFFER CACHE的管理还存在另外两个重要的数据结构:hash bucket和cache buffer chain。
具体的LRU算法不多说了,下面就BUFFER的原理及使用做个简单介绍:
1、当一个SERVER进程需要读数据到BUFFER CACHE中时,首先会判断BUFFER是否存在,如果存在且可用,则获取该数据,如果BUFFER中不存在该数据,则需要从数据文件中读取。
2、在读取数据之前,SERVER进程需要扫描LRU LIST需找FREE的BUFFER,扫描过程中SERVER进程会把发现的所有已经被修改过的BUFFER移动到CHECKPOINT QUENE上,这些DIRTY BUFFER随后可以被写出到数据文件中。
3、如果CHECKPOINT QUENE超过了阀值,SERVER进程就会通知DBWn去写出脏数据,这是触发DBWn写的一个条件,这个阀值是25%,也就是检查点队列超过25%时就会触发DBWn的写操作。
SQL> select kvittag,kvitval,kvitdsc from x$kvit
2 where kvittag='kcbldq';
KVITTAG KVITVAL
---------------------------------------------------------------- ----------
KVITDSC
----------------------------------------------------------------
kcbldq 25
large dirty queue if kcbclw reaches this
如果SERVER进程扫描LRU超过一个阀值仍然不能找到足够的FREE BUFFER,将停止需找,转而通知DBWn去写出脏数据,释放内存空间。这个阀值是40%
SQL> select kvittag,kvitval,kvitdsc from x$kvit
2 where kvittag='kcbfsp';
KVITTAG KVITVAL
---------------------------------------------------------------- ----------
KVITDSC
----------------------------------------------------------------
kcbfsp 40
Max percentage of LRU list foreground can scan for free
4、找到足够的BUFFER之后,SERVER进程就可以将数据从数据文件读入BUFFER CACHE中。
5、如果读取的BLOCK不满足读一致性需求,则SERVER进程需要通过当前BLOCK版本和回滚段构造前镜像返回给用户。
从ORACLE 8i开始LRU LIST和DIRTY LIST又分别增加了辅助LIST(AUXILIARY LIST),引入辅助LIST之后,当数据库初始化时,BUFFER首先存放在LRU的辅助LIST上,当被使用后移动到LRU住LIST上(MAIN PRL_LIST),这样用户进程搜索FREE BUFFER时,就可以从LRU-AUX LIST开始,而DBWR搜索DIRTY BUFFER时,则可以从LRU-MAIN LIST开始,从而提高搜索效率和数据库性能。
可以通过如下命令转储BUFFER CACHE内容,从而清晰地看到以上描述的数据结构:
alter session set events 'immediate trace name buffers level n';
不同LEVEL转储的内容详细程度不同,此命令可用级别主要有1~10级,各级别含义如下:
LEVEL 1:仅包含BUFFER HEADERS信息。
LEVEL 2:包含BUFFER HEADERS和BUFFER概要信息转储。
LEVEL 3:包含BUFFER HEADERS和完整BUFFER内容转储。
LEVEL 4:LEVEL1 + LATCH转储 + LRU队列。
LEVEL 5:LEVEL4 + BUFFER概要信息转储。
LEVEL 6和LEVEL 7:LEVEL4 + 完整的BUFFER内容转储。
LEVEL 8:LEVEL4 + 显示users/waiters信息。
LEVEL 9:LEVEL5 + 显示users/waiters信息。
LEVEL 10:LEVEL6 + 显示users/waiters信息。
SQL> alter session set events 'immediate trace name buffers level 4';
会话已更改。
在跟踪文件中找到了我想要看到的:
MAIN RPL_LST Queue header (NEXT_DIRECTION)[24be8c70,24bf3a7c]
24BE8C1C=>247E7BF4=>247E7848=>247E7904=>247E7CB0=>247E79C0=>247E7D6C=>247E7A7C
247E7B38=>247E7E28=>24BE8CD8=>247F7F30=>247F7FEC=>24BE8290=>247E7EE4=>247E7FA0
247E805C=>247E8118=>247E81D4=>247E8290=>247E834C=>247E8408=>247E84C4=>247E8580
.........................
MAIN RPL_LST Queue header (PREV_DIRECTION)[24be8c70,24bf3a7c]
24BF3A28=>24BF3AE4=>24BF3BA0=>24BF3C5C=>24BF3D18=>24BF3DD4=>24BF3E90=>24BF3F4C
24BF4008=>24BF40C4=>24BF4A50=>25FF2ABC=>24BEAD28=>24BEBC94=>24BEC040=>24BEBEC8
24BEB480=>24BEB770=>257E778C=>257E7848=>257E7904=>257E7D6C=>277F947C=>277F976C
.........................
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]
红色部分我们可以清晰的看到LRU主LIST的队列信息,很直观的链表。