InnoDB Buffer Pool改進LRU頁面置換

邴越發表於2023-03-30

 

由於硬碟和記憶體的造價差異,一臺主機例項的硬碟容量通常會遠超於記憶體容量。對於資料庫等應用而言,為了保證更快的查詢效率,通常會將使用過的資料放在記憶體中進行加速讀取。

 

資料頁與索引頁的LRU

資料頁和索引頁的目的在於快取一部分的表資料和索引資料,其資料總量通常會超過緩衝池大小,所以緩衝池中應只緩衝那些經常使用的熱點資料。InnoDB記憶體管理使用的是最近最少使用(Least Recently Used, LRU)演算法。來淘汰最久未使用的資料

在一般的LRU演算法中,當連結串列中的某一個資料被讀取時,將會將其放置於隊首。當新增資料且連結串列已達最大數量時,將連結串列尾部的資料移除,並將新增的資料置於連結串列首部。

 

 

 

 

InnoDB的LRU並沒有使用傳統的雙端連結串列,而是做了改進,這裡有兩個問題:

  • 預讀失效
  • 緩衝池汙染

 

 

最佳化預讀失效

 

由於預讀(Read-Ahead),提前把頁放入了緩衝池,但最終 MySQL 並沒有從頁中讀取資料,稱為預讀失效。

 

Read-Ahead機制

Read-Ahead用於非同步預取buffer pool中的多個page的一個預測行為。

InnoDB使用兩種提前預讀Read-Ahead演算法來提高I/O效能。

 

  • Linear read-ahead 線性預讀

如果一個extent中的被順序讀取的page超過或者等於   innodb_read_ahead_threshold  引數變數時,Innodb將會非同步的將下一個extent讀取到buffer pool中,innodb_read_ahead_threshold可以設定為0-64的任何值(注:innodb中每個extent就只有64個page),預設為56。值越大,訪問模式檢查就越嚴格。

 

  • Random read-ahead 隨機預讀

如果當同一個extent中連續的13個page在buffer pool中發現時,Innodb會將該extent中的剩餘page讀到buffer pool中。控制引數  innodb_random_read_ahead  預設沒有開啟。

 

如何對預讀失效進行最佳化?

要最佳化預讀失效,思路是:

  • 讓預讀失敗的頁,停留在緩衝池LRU裡的時間儘可能短
  • 讓真正被讀取的頁,才挪到緩衝池LRU的頭部

 

 

 

InnoDB 的具體解決方法

 

 

 

由上圖可以看出 InnoDB 將 LRU List 分為兩部分,預設前 5/8 為 New Sublist(新生代)用於儲存經常被使用的熱點資料頁,後 3/8 為 Old Sublist(老生代),新讀入的資料頁預設被放到 Old Sublist 中,只有滿足一定條件後,才會被移入 New Sublist。

 

新生代和老生代代比例在 MySQL 中透過引數 innodb_old_blocks_pct 控制,值的範圍是5到95.預設值是37(即池的3/8)。

  • 如果資料頁真正被讀取(預讀成功),才會加入到新生代的頭部
  • 如果資料頁沒有被讀取,則會比新生代裡的“熱資料頁”更早被淘汰出緩衝池

 

舉個例子,整個緩衝池如圖

 

 

 

 

假如有一個頁號為 50 的資料頁頁被預讀加入緩衝池:

(a). 頁號為50 的資料頁只會從老生代頭部插入,老生代尾部(也是整體尾部)的頁會被淘汰掉,即 8 號資料頁被淘汰。

 

 

 

 

(b). 假如頁號為50 的資料頁不被真正讀取,即預讀失敗,它將比新生代的資料更早淘汰出緩衝池

(c). 假如 50 這一頁立刻被讀取到,例如SQL訪問了頁內的行row資料。它會被立刻加入到新生代的頭部,同時新生代的頁會被擠到老生代,此時並不會有頁面被真正淘汰

 

 

改進版緩衝池LRU能夠很好的解決“預讀失敗”的問題。但仍然無法解決緩衝池被汙染但問題。

 

 

緩衝池汙染

當某一個SQL語句,要批次掃描大量資料時,可能導致把緩衝池的所有頁都替換出去,導致大量熱資料被換出,MySQL 效能急劇下降,這種情況叫緩衝池汙染。

 解決方法

緩衝池加入了一個“老生代停留時間視窗”的機制:

(a). 假設T=老生代停留時間視窗

(b). 插入老生代頭部的頁,即使立刻被訪問,並不會立刻放入新生代頭部

(c). 只有滿足“被訪問”並且“在老生代停留時間”大於T,才會被放入新生代頭部

假如批次資料掃描,有91、92、93、94、95、96、97、98、99等頁面將要依次被訪問

 

 

 

 

 

 

如果沒有“老生代停留時間視窗”的策略,這些批次被訪問的頁面,會置換出大量熱資料。

 

 

加入“老生代停留時間視窗”策略後,短時間內被大量載入的頁,並不會立刻插入新生代頭部,而是優先淘汰那些,短期內僅僅訪問了一次的頁。

 

 

只有在老生代呆的時間足夠久,停留時間大於T,才會被插入新生代頭部。

 

 

老生代的停留時間由引數 innodb_old_blocks_time 控制,單位為毫秒,預設是1000

 

總結

  1. 緩衝池(buffer pool)是一種常見的降低磁碟訪問的機制
  2. InnoDB的緩衝池以資料頁(page)為單位快取資料
  3. InnoDB 對普通 LRU 進行了最佳化,
  • 將緩衝池分為老生代和新生代,入緩衝池的頁,優先進入老生代,頁被訪問,才進入新生代,以解決預讀失效的問題。
  • 同時採用老生代停留時間視窗機制,當資料頁被訪問且在老生代停留時間超過配置閾值的,才進入新生代,以解決批次資料訪問,大量熱資料淘汰的問題
 

相關文章