InnoDB 緩衝池LRU策略及關鍵特性

內存

緩衝池

緩衝池簡單來說就是一塊內存區域,通過內存的速度來彌補磁盤速度較慢對數據庫性能的影響。在數據庫中讀取頁的操作,首先將從磁盤讀取的頁存放在緩衝池中,這個過程稱爲將頁 “FIX” 在緩衝池中。下一次再讀取相同的頁時,首先判斷該頁是否在緩衝中。若在則命中,否則讀取磁盤上的頁。

對於數據庫中的頁的修改,首先修改在緩衝池中的頁,然後再以一定的頻率刷新到磁盤上,頁衝緩衝池刷新回磁盤的操作並不是在每次頁發送更新時觸發,而是通過一種稱爲 Checkpoint 的機制刷新回磁盤。這是爲了提高數據庫的整體性能。

LRU List、Free List 和 Flush List

緩衝區是一個很大的內存區域,其中存放各種類型的頁,那麼InnoDB 存儲引擎怎麼進行管理呢?

通常數據庫中的緩衝池通過 LRU(最近最少使用)算法來進行管理的。最頻繁使用的頁在 LRU 列表的前端,而最少使用的頁在 LRU 列表的尾端。當緩衝池不能存放新讀取到的頁時,將首先釋放 LRU 列表中尾端的頁。

緩衝池中的頁大小默認爲 16KB。但是 InnoDB 存儲引擎對傳統的 LRU 算法做了一些優化。 LRU 列表中加入了 midpoint 位置。新讀取到的頁,雖然是最新訪問的頁,但並不是直接放入到 LRU 列表的首部,而是插入到 LRU 列表的 midpoint 位置。在默認配置下,該位置在 LRU 列表長度的 5/8 處。在 InnoDB 存儲引擎中,把 midpoint之後的列表稱爲 old列表,之前的列表稱爲 new 列表。可以簡單地理解爲 new 列表中的頁都是最活躍的熱點數據。

爲什麼要這樣優化?如果直接講讀取的頁放入到 LRU 列表的首部會有什麼問題?

如果將直接讀取的頁放入到 LRU 的首部,那麼某些 SQL 操作可能會使緩衝池中的頁被刷新出來,從而影響緩衝池的效率。常見的這類操作作爲索引或數據的掃描操作。這類操作需要訪問表中的許多頁,甚至全部的頁,而這些頁通常來說又僅在這次查詢操作中需要,並不是活躍的熱點數據。如果頁被放入 LRU 列表的首部,那麼非常可能將所需要的熱點數據頁從 LRU 列表中移除,而在下一次需要讀取改頁時,InnoDB 存儲引擎需要再次訪問磁盤。


執行命令 SHOW ENGINE INNODB STATUS;

Buffer pool size 共有 8192 個頁

Free buffefreers表示free列表中頁的數量

Database pages表示LRU列表中頁的數量

Database pages與Free buffers之和不等於Buffer pool size,因爲還可能分配給自適應哈希索引、lock信息、insert buffer等頁。
因爲是本地環境,沒有做更新操作,所以即使設置了innodb_old_blocks_time,not young還是爲0。
buffer pool hit rate,表示緩衝池命中率,一般不低於95%,如果偏低,要看看是不是有全表掃描造成LRU列表污染。

Free List

數據庫剛啓動的時候,LRU 列表爲空,此時需要用到的時候直接將Free列表中的頁刪除,在LRU列表中增加相應的頁,維持頁數守恆。

Flush List

LRU列中數據被修改後,產生髒頁。數據庫通過checkpoint機制將髒頁刷新會磁盤,flush list中的頁即爲髒頁列表。髒頁即存在於LRU中,也存在於Flush中。LRU list用於管理緩衝池中頁的可用性,Flush list用於將頁刷新回磁盤。

Checkpoint技術

緩衝池的設計目的是爲了協調 CPU 速度與磁盤速度的鴻溝,因此頁的操作首先都是在緩衝池中完成。倘若每次一個頁發生變化,就將新野的版本刷新到磁盤,那麼這個開銷是非常大的。若熱點數據集中的某幾個頁中,那麼數據庫的性能將變得非常差。同時,如果在從緩存池將頁的新版本刷新到磁盤時發生宕機,那麼數據就不能恢復了。爲了避免發生數據丟失的問題,當前事務數據庫系統普遍都採用 Write Ahead Log 策略,即當事務提交時,先寫重做日誌,再修改頁。當由於發生宕機而導致數據丟失,通過重做日誌來完成數據的恢復。

Checkpoint技術目的是結局以下幾個問題:

  1. 縮短數據庫的恢復時間
  2. 緩衝池不夠用時,將髒頁刷新到磁盤
  3. 重做日誌不可用時,刷新髒頁

當數據庫發生宕機時,數據庫不需要重做所以的日誌,因爲 Checkpoint 之前的頁都已經刷新回磁盤。故數據庫只需對 Checkpoint 後的重做日誌進行恢復。這樣就大大縮短了恢復的時間。

當前事務數據庫系統對重做日誌的設計都是循環使用的,當數據庫發生宕機時,數據庫恢復操作不需要這部分的重做日誌,因此這部分就可以被覆蓋重用。

在 InnoDB 存儲引擎內部,有兩種 Checkpoint, 分別爲:

  1. Sharp Checkpoint 發生在數據庫關閉時將所有的髒頁都刷新回磁盤
  2. Fuzzy Checkpoint 數據庫在運行時只刷新一部分髒頁,而不是刷新所有的髒頁回磁盤

InnoDB 關鍵特性

  1. 插入緩衝(Insert Buffer)
  2. 兩次寫
  3. 自適應哈希索引
  4. 異步 IO
  5. 刷新鄰接頁

Insert Buffer: 對於非聚集索引的插入或更新操作,不是每一次直接插入到索引頁中,而是先判斷插入的非聚集索引頁是否在緩衝池中,若在,則直接插入,若不在,則先放入到一個 Insert Buffer 對象中,然後再以一定的頻率和情況進行 Insert Buffer 和輔助索引頁子節點的 merge(合併)操作,這時通常能將多個插入合併到一個操作中去,這就大大提高了對於非聚集索引插入的性能。

Insert Buffer 的使用需要同時滿足以下兩個條件:

  1. 索引是輔助索引
  2. 索引不是唯一的

若是索引唯一索引,那麼在插入時需要判斷插入的記錄是否是唯一,這需要讀取輔助索引頁,而insert buffer 的設計就是避免讀取insert buffer,這會導致失去insert buffer 的設計意義。

doublewrite : Insert Buffer 帶給 InnoDB 存儲引擎的是性能上的提升,而doublewrite 帶給 InnoDB 存儲引擎的是數據頁的可靠性。

自適應哈希索引:InnoDB 存儲引擎會監控對錶上各索引頁的查詢。如果觀察到建立哈希索引可以帶來速度提升,則建立哈希索引,稱之爲自適應哈希索引。

異步IO:爲了提高磁盤操作性能,與 AIO 對應的是 Sync IO,即每進行一次 IO操作,需要等待此次操作結束才能繼續接下來的操作。如果用戶發出的是一條索引掃描的查詢,那麼這條 SQL 查詢語句可能需要掃描多個索引頁,也就是需要進行多次的 IO 操作。在每掃描一個頁等待其完成後再進行下一次的掃描,這是沒有必要的。用戶可以在發出一個 IO 請求後立即再發出另一個 IO 請求,當全部 IO 請求發送完畢後,等待所有 IO 操作的完成,這就是 AIO。

刷新鄰接頁:當刷新一個髒頁時,InnoDB 存儲引擎會檢測該頁所在的區(extent)的所有頁,如果是髒頁,那麼一起進行刷新。

參考

MySQL 技術內幕 InnoDB 存儲引擎

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