InnoDB Buffer Pool改進LRU頁面置換

 

由於硬盤和內存的造價差異,一臺主機實例的硬盤容量通常會遠超於內存容量。對於數據庫等應用而言,爲了保證更快的查詢效率,通常會將使用過的數據放在內存中進行加速讀取。

 

數據頁與索引頁的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 進行了優化,
  • 將緩衝池分爲老生代和新生代,入緩衝池的頁,優先進入老生代,頁被訪問,才進入新生代,以解決預讀失效的問題。
  • 同時採用老生代停留時間窗口機制,當數據頁被訪問且在老生代停留時間超過配置閾值的,才進入新生代,以解決批量數據訪問,大量熱數據淘汰的問題
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章