1. 程式人生 > 實用技巧 >Buffer Cache內部原理-LRU佇列-主輔LRU

Buffer Cache內部原理-LRU佇列-主輔LRU

LRU佇列

LRU佇列
1.主LRU和輔LRU連結串列
2.物理讀時訪問LRU連結串列情況
3.輔助LRU為空後的處理方式toc
LRU分為LRU和LRUW,他們兩個分別分為主輔兩個連結串列。也就是說一組LRU包含四個連結串列,主LRU,輔LRU,主LRUW,輔LRUW。其中主LRU和輔LRU用於在Buffer cache中尋找可以覆蓋的buffer cache塊。主LRUW和輔LRUW的作用和 檢查點佇列類似或者說是二者合力而為,是DBWR用來寫髒塊的。

1.主LRU和輔LRU連結串列

作用:物理讀時,伺服器程序將資料塊從資料檔案讀入Buffer Cache中,那麼程序應該將資料塊讀進buffer cache的哪個地方呢?讀入的一定是最不常用的Buffer cache。覆蓋掉最不常用的就是LRU的本質作用

那麼LRU是如何找到這個最不常用的Buffer呢?

LRU會將Buffer Cache中所有的Buffer都串聯在一起。

LRU又分成兩條連結串列,主LRU和輔LRU。其中主LRU又分成冷端和熱端兩個部分。每個Buffer都會有一個訪問計數TCH,TCH以3s為一個階段,每個階段只要又程序訪問,它的數值就會加1。如果3s內訪問多次,那麼也只會加1。Buffer在冷端還是在熱端主要靠這個TCH。

如圖所示,每個字母的下面都有一個數字就是TCH,這裡面每個node都指向buffer cache中的某個buffer,圖中有6個buffer為主LRU連結串列兩個buffer為輔助LRU連結串列。一般情況下,輔助LRU會佔LRU buffer總數量的25%,主LRU存放剩下的75%(但不是硬性規定可以變化)

。主LRU又分為兩段冷端和熱端上圖已經標明。注意Buffer Cache中所有的buffer都存放在LRU中但是不一定都在主,輔LRU中還有LRUW。

主輔LRU數量的比例可以通過如下的方式進行檢視:

SYS@proe>select CNUM_SET,CNUM_REPL,ANUM_REPL from x$kcbwds;


  CNUM_SET  CNUM_REPL  ANUM_REPL
---------- ---------- ----------
         0          0          0
         0          0          0
         0          0          0
         0          0          0
         0          0          0
         0          0          0
         0          0          0
         0          0          0
     20186      20186      17401
     20180      20180      17378
     20180      20180      17388


  CNUM_SET  CNUM_REPL  ANUM_REPL
---------- ---------- ----------
     20183      20183      17392
         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
         0          0          0


  CNUM_SET  CNUM_REPL  ANUM_REPL
---------- ---------- ----------
         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
         0          0          0


32 rows selected.

注意:在oracle中任何連結串列都有專門的latch對其進行保護,一組LRU連結串列,包括主LRU,輔LRU,主LRUW和輔LRUW,它們被稱為一個Workset(工作組),被一個latch保護稱為:cache buffers lru chain

上面的語句執行結果共有32行說明有32個工作組,但是隻有4行存在資料其他都是0這代表了只有4個工作組被使用。相應的cache buffers lru chain的數量也是32個只有4個被使用,其餘的都是空閒狀態。結果中的列的含義如下:CNUM_SET列是工作組中Buffer的總數量;CNUM_REPL是工作組內所有LRU中的Buffer數量,通常這兩個值是相等的。ANUM_REPL是輔助LRU中Buffer的數量。使用CNUM_REPL減去ANUM_REPL就是主LRU中Buffer的數量。

可以使用如下方式去計算:

#計算主LRU中Buffer的數量
SYS@proe>select sum(CNUM_REPL)-sum(ANUM_REPL) from x$kcbwds;


SUM(CNUM_REPL)-SUM(ANUM_REPL)
-----------------------------
                        11211

#計算輔助LRU中buffer的佔比
SYS@proe>select round(sum(ANUM_REPL)/sum(CNUM_SET)*100,2) from x$kcbwds;


ROUND(SUM(ANUM_REPL)/SUM(CNUM_SET)*100,2)
-----------------------------------------
                                    86.11

2.物理讀時訪問LRU連結串列情況

伺服器程序要讀5號檔案的80號塊,但是buffer cache中並不存在。

整個過程如下:

程序先搜尋hash表,搜尋結果沒找到,5號檔案的80號塊並不存在buffer cache中。程序發起物理讀將其讀入buffer cache中。物理讀開始,程序首先要獲得cache buffers lru chain latch然後程序從輔助LRU的尾端搜尋可以覆蓋的Buffer。覆蓋標準是,不是髒塊同時TCH值小於2。伺服器程序在找到輔助LRU中buffer H,TCH為1說明這個塊在某個3s中被若干次訪問。然後輔助LRU中的H被伺服器程序認為可覆蓋,會被從輔助連結串列移動到主連結串列的冷端頭部。如下所示,

這個時候輔助LRU連結串列上就只存在一個塊了。H被移到了冷端頭,然後cache buffers lru chain latch被釋放,LRU鏈的相關操作就結束了,伺服器程序會真正的進行一次物理讀將需要的資料從物理檔案上讀入Buffer Cache中。

整個過程需要注意:程序是從輔助連結串列開始搜尋LRU的,找到覆蓋的buffer後會將它移動到主LRU的冷端頭。輔助LRU可以為空,上圖中如果再發生一次物理讀G對應的buffer就會被覆蓋,同時G也會移動到主鏈的冷端頭,當然這種情況只會發生在物理讀繁忙的時候。也就是下圖所示的情況,第二次物理讀。

當輔助LRU為空時,發生物理讀是從哪裡搜尋可用塊呢?從主LRU的冷端尾部。也就是上圖中的F處但是這個F並不符合塊覆蓋的原則,因為它的TCH大於2。所以這個塊並不能被覆蓋。並且它會被移動到主LRU的熱端頭同時將其TCH置為0,如下所示

這樣原來的熱端尾的C變成了冷端頭,然後程序會繼續從冷端尾向冷端頭去掃描,冷端尾目前是E塊,但是它是一個髒塊也不可以覆蓋,但是它不會被移動而是被放入LRUW中這裡就說為跳過。接下來掃描到的是D,它將會被覆蓋。D會被移動到冷端頭,也就是C之前,如下所示。

D所對應的Buffer會被新的物理讀所覆蓋,因此D又被稱為“犧牲者”,LRU的規則就是選擇犧牲者的規則。以後如果再有物理讀,那麼被覆蓋的Buffer將依次是H、G、C、D迴圈往復,但是處於熱端的塊不會被使用。只會迴圈使用冷端的。

總結:

1)程序從輔助LRU連結串列尾開始搜尋犧牲者。

2)如果輔助LRU連結串列為空,或者輔助LRU上沒有可用塊,都是髒塊。將會從LRU主鏈的冷端尾開始搜尋犧牲者。

3)找到可以覆蓋的犧牲者後,將它移動到主LRU的冷端頭,它所對應的buffer被新的物理讀所覆蓋。

4)髒塊會被跳過而不是移動。

5)在搜尋過程中TCH大於或等於2的buffer會被移動到熱端頭部。

3.輔助LRU為空後的處理方式

上面也說過輔助LRU在物理讀很頻繁的時候可能為空。為空後如何處理呢?不可能會一直為空的,相關的程序就是SMON。

SMON和PMON,CKPT,DBWR,LGWR一樣都是每3s醒來一次,SMON每次醒來都會申請cache buffers lru chain latch鎖資源,然後檢查主LRU和輔LRU的長度,如果輔助LRU中的Buffer小於25%,SMON會從主LRU冷端尾搜尋TCH小於2的非髒塊。將其移動到輔助LRU中保持輔助LRU25%的佔比。所以在一般情況下,才會有主和輔LRU長度是3比1的關係。對於物理讀很多的場景輔助LRU的佔比可能會稍低一些。

OracleLRU分為主LRU和輔LRU的目的是加快搜索LRU連結串列的速度。主LRU中存在各種各樣的Buffer,而SMON會每3秒一次將主LRU中非髒塊,TCH小於2的可覆蓋buffer移動到輔LRU上,程序需要時在輔LRU上尋找犧牲者速度一定會快很多。縮短了搜尋LRU的時間,也就減少了cache buffers lru chain latch的持有時間。

除此之外,資料庫在剛啟動的時候或者剛剛lflush了Cache Buffer後,所有的Buffer都會在輔助LRU中。測試方法如下

SYS@proe>alter system flush buffer_cache;


System altered.


SYS@proe>select CNUM_SET,CNUM_REPL,ANUM_REPL from x$kcbwds where cnum_set > 0;


  CNUM_SET  CNUM_REPL  ANUM_REPL
---------- ---------- ----------
     20186      20186      20186
     20180      20180      20180
     20180      20180      20180
     20183      20183      20183
#過一會再檢視
SYS@proe>select CNUM_SET,CNUM_REPL,ANUM_REPL from x$kcbwds where cnum_set > 0;


  CNUM_SET  CNUM_REPL  ANUM_REPL
---------- ---------- ----------
     20186      20186      20154
     20180      20180      20149
     20180      20180      20148
     20183      20183      20150

注意更新後立即檢視,可以看到輔助LRU存放的buffer數量和全部的是一致的,也就是所有的Buffer都在輔助LRU中。


​ 物理讀時伺服器會先掃描輔助LRU,將找到的犧牲者移動到主LRU冷端頭進行覆蓋。因此當資料庫在正常運轉的時候,輔助LRU中的Buffer數將不斷減少,因為不斷有物理讀,所以輔LRU上的buffer會不斷被移動到主LRU上,主LRU的長度也會相應不斷增長。而SMON程序會在輔助LRU的buffer佔比低於25%時,將主LRU中的Buffer移動到輔助LRU上維持一個平衡。




來自為知筆記(Wiz)