Innodb的LRU列表解析

Innodb爲了加快對磁盤中數據的操作,在操作磁盤上的數據時,會先把數據存放到一塊名爲Buffer Pool的內存緩衝池中(緩衝池以頁爲單位進行緩存,頁大小爲16K)。

由於受到機器限制,內存的大小遠小於磁盤的大小,因此需要一種機制來淘汰非熱點數據,保證內存中存在的數據是較爲頻繁訪問的數據。

其中LRU是這種管理場景下最常用的算法,LRU算法的思想爲:

  1. 新數據插入到鏈表頭部;
  2. 每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;
  3. 當鏈表滿的時候,將鏈表尾部的數據丟棄。

這樣就能夠保證熱點數據位於LRU的頭部,而非熱點數據隨着其他數據的加入最終在鏈表尾部被丟棄。在實現該算法時一般會針對程序的特性對算法進行修改。

那麼在實現LRU的過程中Innodb有以下需要注意的地方:

  1. 當數據庫針對一張大表進行全表查詢的時候,按照樸素的LRU算法,所有的數據都會被放入LRU鏈表中。從而將其他查詢語句留下的高頻數據擠到LRU尾部並丟棄。

  2. LRU中爲了避免對磁盤的多次讀取而實現了預讀機制,預讀機制會額外讀取一些可能會被讀取的頁。

    • 線性預讀:當一個區中有連續56個頁面(56爲默認值,一個區64頁)被加載到BufferPool中,會將這個區中的所有頁面都加載到BufferPool中。
    • 隨機預讀:當一個區中隨機13個頁面(13爲默認值)被加載到BufferPool中,會將這個區中所有頁面都加載到BufferPool中。隨機預讀默認是關閉,由變量innodb_random_read_ahead控制。

    按照樸素的LRU算法,這些數據都會被放入LRU鏈表中。從而將其他查詢語句留下的高頻數據擠到LRU尾部並丟棄。

如果以上產生的數據僅僅在這次查詢中需要,並不是活躍的熱點數據,那就會導致緩存命中率下降。

爲了解決以上提到的缺點,InndoDB使用了以下兩個策略:

  1. 將LRU鏈表劃分爲old區與young區,young區位於鏈表的頭部,負責存放經常被訪問的高頻數據,而old區位於鏈表尾部,負責存放不經常訪問的熱點數據。而兩個區的交匯點稱爲midpoint。Innodb中使用innodb_old_blocks_pct來劃分兩個區,該參數表示old區的比例。剛進來的數據存放在midpoint位置的old區域。
  2. 劃分區域後需要一個策略來判斷數據何時從old區進入young區。Innodb定義了參數innodb_old_blocks_time,單位爲毫秒。當old區中某個頁的最後查詢時間跟第一次查詢時間差超過該參數,該頁就會從old區進入young區。這個策略避免了只查詢一次或只在短暫的時間查詢多次的數據進入young區,防止熱數據被刷出內存。

有了以上策略,就能清楚知道Innodb如何使用LRU列表管理讀取的頁:

  1. 掃描過程中,若需要新插入數據頁,首先從Free列表中查詢是否有可用的空閒頁。

    • 有可用空閒頁:將該頁從Free列表中刪除,放入到LRU列表中。
    • 無可用空閒頁:淘汰LRU列表末尾的頁,將該內存空間分配給新的頁。

    將新的頁插入到midpoint位置的old區域。

  2. 掃描過程中,若需要更新數據頁,根據參數innodb_old_blocks_time判斷是否將old區數據更新到young區。

    • 最後查詢時間跟第一次查詢時間差超過該參數,則說明多次訪問該數據,則將其從old區加入young區,該操作稱爲page made young。
    • 最後查詢時間跟第一次查詢時間差沒有超過該參數,比如只查詢了一次,或者一次查詢該頁的多個數據。這些操作的時間極短且頻率低。不會將其加入young區,該操作稱爲page not made young。

通過命令SHOW ENGINE INNODB STATUS可以觀察LRU列表的使用情況。

Databases pages:表示LRU列表中頁的數量。

pages made young:表示LRU列表中頁移動到young區的次數。

not young:表示LRU列表中頁沒有移動到young區的次數。

youngs/s 、non-youngs/s:表示了每秒這兩類操作的次數。

Buffer pool hit rate:表示緩衝池的命中率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章