簡單來講,Oracle為了高效管理BUFFER CACHE主要使用以下2種LRU列:
?LRU列,又叫替換列(replacement list),其中又分為主列和輔助列。
主列:已使用的緩沖區列,分為HOT和COLD區域。HOT區域中存放著使用頻率高的數據塊,COLD區域存放使用頻率較低的數據塊。Oracle用TCH表示該區域內數據塊的使用頻繁程度。
輔助列:空閑緩沖區列。數據庫重啟之后,所有的緩沖區頭開始時都是在輔助列中管理的。或者在LRU-W上的臟數據寫進制數據文件之后,Oracle會將相關的緩沖區頭直接掛載至輔助列上。輔助列上的緩沖區頭表示可以直接被其他數據塊使用。
?LRU-W列,又叫寫入列(LRU-Write list),臟數據列。臟緩沖區頭由DBWR進程從LRU列移動至LRU-W列中(有時DBWR進程因為太忙而不能執行這種移動)。尋找空閑緩沖區的服務器進程在LRU列表上掃描時,如果遇到了臟緩沖區頭,就將臟緩沖頭從LRU列表中移動至LRU-W列表中。LRU-W列中又分為主列和輔助列。
主列:已修改的緩沖區列。
輔助列:當前正通過DBWR進程寫入的緩沖區列。
提示 除了LRU列和LRUW列,還有LRU-XO列、LRU-XR列、LRU-P列。在性能優化時,最容易引起性能問題的是LRU列和LRU-W列,所以一般情況下并不需要關心LRU-XO列、LRU-XR列、LRU-P列,所以本章也不打算深入講解這3列。
LRU列和LRW-W列總是成對(pair)的出現,稱之為WORKING SET。為了提高性能,Oracle使用多個WORKING SET,由CACHE BUFFERS LRU CHAIN LATCH保護。基于性能上的考慮,服務器進程在檢索空閑緩沖區時,首先會檢索LRU列的輔助列,當輔助列沒有剩余緩沖區時,才會檢索LRU主列中的COLD區域。
另外需要指出的是LRU列中HOT和COLD區域只是針對nK BUFFER POOL的,KEEP POOL和RECYCLE POOL中沒有HOT和COLD區域之分。KEEP POOL比較適合存放使用頻繁讀取的小表,RECYCLE POOL比較適合存放使用頻率較低的大表。數據塊在nK BUFFER POOL中的存放位置受以下隱含參數的影響:
?隱含參數_db_percent_hot_default,默認值為50。表示SINGLE BLOCK I/O(如唯一鍵索引讀)讀取的數據塊進入BUFFER CACHE之后,會將其放置在nK BUFFER POOL中LRU列的中間,即“熱”端的尾部。MULTI BLOCK I/O(如對大表進行全表掃描)時,服務器進程會將大量的數據塊讀至BUFFER CACHE中,為了減輕對LRU列表中數據塊(熱塊)的沖擊,Oracle會將大表全表掃描的數據塊存放至LRU列表的尾部,存放在LRU列表尾部的數據塊可以盡快地被交換出BUFFER CACHE。
?隱含參數_db_percent_hot_keep和_db_percent_hot_recycle,默認值為0。表示數據塊進入到KEEP POOL和RECYCLE POOL時放在LRU列的尾端。
前面提到,Oracle為了提高性能會使用多個WORKING SET,并由CACHE BUFFERS LRU CHAIN LATCH保護。每一個CACHE BUFFERS LRU CHAIN LATCH對應一個WORKING SET,接下來將相對深入地探討一下CACHE BUFFERS LRU CHAIN LATCH。
可以通過以下方法獲取當前系統中CACHE BUFFERS LRU CHAIN LATCH的數量:
?查詢隱含參數_db_block_lru_latches。
?通過查詢V$LATCH_CHILDREN獲得。如下所示:
SQL> select count(*) from v$latch_children2 where name='cache buffers lru chain';
Oracle默認創建的CACHE BUFFERS LRU CHAIN LATCH的數量跟CPU的個數和DB_WRITER_PROCESSES參數有關。若DBWR數小于4,則創建4CPU_COUNT個CACHE BUFFERS LRU CHAIN LATCH,若DBWR數大于4,則創建DB_WRITER_PROCESSESCPU_COUNT個CACHE BUFFERS LRU CHAIN LATCH。從以上算法可以看出,CACHE BUFFERS LRU CHAIN LATCH的數量總是大于DBWR數量,即WORKING SET的數量總是大于DBWR的數量。不同的WORKING SET可以出現在不同類型的BUFFER POOL中,每個BUFFER POOL獨立使用自己的CACHE BUFFERS LRU CHAIN LATCH
由于一個數據庫實例可以配置8種不同類型的BUFFER POOL(DEFAULT、2KB、4KB、8KB、16KB、32KB、KEEP、RECYCLE BUFFER POOL),因此CACHE BUFFERS LRU CHAIN LATCH的數量至少為8個。可以通過以下查詢獲得各個BUFFER POOL中CACHE BUFFERS LAU CHAIN LATCH的使用情況:
SQL> select d.blk_size,c.child#,p.bp_name,c.gets,c.sleeps
2 from x k c b w d s d , v kcbwds d, v kcbwdsd,vlatch_children c, x$kcbwbpd p
3 where d.set_latch=c.addr
4 and d.set_id between p.bp_lo_sid and p.bp_hi_sid
5 order by c.child#;
BLK_SIZE CHILD# BP_NAME GETS SLEEPS
8192 1 KEEP 459 08192 2 KEEP 459 08192 3 RECYCLE 459 08192 4 RECYCLE 459 08192 5 DEFAULT 410618 28192 6 DEFAULT 402282 02048 7 DEFAULT 459 02048 8 DEFAULT 459 04096 9 DEFAULT 459 04096 10 DEFAULT 459 08192 11 DEFAULT 459 08192 12 DEFAULT 459 016384 13 DEFAULT 459 016384 14 DEFAULT 459 032768 15 DEFAULT 459 032768 16 DEFAULT 459 0
16 rows selected.
從上面的查詢結果可以看出,目前系統存在16個CACHE BUFFERS LRU CHAIN LATCH,但只使用了2個。以下2種情況下必須要獲得CACHE BUFFERS LRU CHAIN LATCH:
?數據塊讀進BUFFER CACHE之前需要獲得CACHE BUFFERS LRU CHAIN LATCH查找空閑緩沖區。
?DBWR進程為了獲得臟數據塊列表,掃描LRU-W列之前需要獲得CACHE BUFFERS LRU CHAIN LATCH。此外將空閑緩沖區移動至LRU輔助列,也需要獲得CACHE BUFFERS LRU CHAIN LATCH。
當許多進程同時檢索LRU列或者LRU-W列時,則容易出現LATCH: CACHE BUFFERS LRU CHAIN等待事件。一般來講,CACHE BUFFERS LRU CHAIN LATCH爭用最主要的原因是低效的SQL導致前臺進程過多地請求空閑緩沖區引起的。與發生LATCH:CACHE BUFFERS CHAINS等待事件類似,不能簡單地通過調整隱含參數_db_block_lru_latches來解決LATCH: CACHE BUFFERS LRU CHAIN等待事件。
在這里,讀者需要仔細體會CACHE BUFFERS CHAINS LATCH和CACHE BUFFERS LRU CHAIN LATCH之間的區別:
?多個會話并發訪問相同的表或者索引時,則發生CACHE BUFFERS CHAINS LATCH爭用的概率較高,因為相同表或索引的數據塊多集中于幾條相同的HASH CHAIN中。
?多個會話并發訪問不同的表或者索引時,則發生CACHE BUFFERS LRU CHAIN LATCH爭用的概率較高,因為不同的數據塊都存放在BUFFER CACHE中的概率較低,當發生物理讀時需要更多地掃描LRU列和LRU-W列。