1. 程式人生 > 其它 >Mysql Buffer Pool

Mysql Buffer Pool

Buffer Poll

Buffer Pool 中預設的快取頁大小和在磁碟上預設的頁大小是一樣的,都是 16KB,為了更好的管理這些在 Buffer Pool 中的快取頁,設計 InnoDB 的大叔為每一個快取頁都建立了一些所謂的 控制資訊 ,這些控制資訊 包括該頁所屬的表空間編號、頁號、快取頁在 Buffer Pool 中的地址、連結串列節點資訊、一些鎖資訊以及 LSN 資訊。我們就把每個頁對應的控制資訊佔用的一塊記憶體稱為一個 控制塊吧。控制塊和快取頁是一一對應的,它們都被存放到 Buffer Pool 中,其中控制塊被存放到 Buffer Pool 的前邊,快取頁被存放到 Buffer Pool 後邊

,所以整個 Buffer Pool 對應的記憶體空間看起來就是這樣的:

Buffer Pool裡快取的不只有索引頁和資料頁(佔很大一部分),還有undo頁,自適應雜湊索引,插入緩衝,鎖資訊等等。

Buffer Pool裡有三個連結串列,LRU連結串列,free連結串列,flush連結串列。

free連結串列的節點是所有空閒的快取頁對應的控制塊。

我們怎麼知道該頁在不在 Buffer Pool 中呢?

雜湊,我們可以用 表空間號 + 頁號 作為 key , 快取頁 作為 value 建立一個雜湊表,在需要訪問某個頁的資料 時,先從雜湊表中根據 表空間號 + 頁號 看看有沒有對應的快取頁,如果有,直接使用該快取頁就好,如果沒 有,那就從 free連結串列 中選一個空閒的快取頁,然後把磁碟中對應的頁載入到該快取頁的位置。

flush連結串列裡的節點是所有髒頁的控制塊。

LRU連結串列:

  1. 一個簡單的lru連結串列是

    • 如果該頁不在 Buffer Pool 中,在把該頁從磁碟載入到 Buffer Pool 中的快取頁時,就把該快取頁對應的 控制塊 作為節點塞到連結串列的頭部。
    • 如果該頁已經快取在 Buffer Pool 中,則直接把該頁對應的 控制塊 移動到 LRU連結串列 的頭部。
  2. 但這會有一些問題:

    • innodb提供了預讀功能,預讀 又可以細分為下邊兩種:

      • 線性預讀:設計 InnoDB 的大叔提供了一個系統變數 innodb_read_ahead_threshold ,如果順序訪問了某個區 ( extent )的頁面超過這個系統變數的值,就會觸發一次 非同步
        讀取下一個區中全部的頁面到 Buffer Pool 的請求。
      • 隨機預讀:如果 Buffer Pool 中已經快取了某個區的13個連續的頁面,不論這些頁面是不是順序讀取的,都會觸發 一次 非同步 讀取本區中所有其的頁面到 Buffer Pool 的請求。預設是關閉的。

      這樣如果預讀的這樣頁都沒有被用到的話,就會導致真正的熱點頁被排擠掉

    • 全表掃描:掃描全表意味著什麼?意味著將訪問到該表所在的所有頁!假設這個表中記錄非常多的話,那該表會佔用特 別多的 頁 ,當需要訪問這些頁時,會把它們統統都載入到 Buffer Pool 中,這也就意味著吧唧一下, Buffer Pool 中的所有頁都被換了一次血。這會大大降低命中率

  3. 於是設計 InnoDB 的大叔把這個 LRU連結串列 按照一定比例分成兩截,分別是:

    • 一部分儲存使用頻率非常高的快取頁,所以這一部分連結串列也叫做 熱資料 ,或者稱 young區域 。
    • 另一部分儲存使用頻率不是很高的快取頁,所以這一部分連結串列也叫做 冷資料 ,或者稱 old區域 。

    但注意:我們是按照某個比例將LRU連結串列分成兩半的,不是某些節點固定是young區域的,某 些節點固定是old區域的,隨著程式的執行,某個節點所屬的區域也可能發生變化。預設情況下old區佔比是3/8。

    然後規定:

    • 當磁碟上的某個頁面在初次載入到Buffer Pool中的某個快取頁時,該快取頁對應 的控制塊會被放到old區域的頭部,這樣針對預讀到 Buffer Pool 卻不進行後續訪問的頁面就會被逐漸從 old 區域逐出,而不會影響 young 區域中被使用比較頻繁的快取頁。
    • 並且在對某個處在 old 區域的快取頁進行第一次訪問時就在它對應的控制塊中 記錄下來這個訪問時間,如果後續的訪問時間與第一次訪問的時間在某個時間間隔內,那麼該頁面就不會被 從old區域移動到young區域的頭部,否則將它移動到young區域的頭部。

    還有一個優化是:只有被訪問的快取頁位於 young 區域的 1/4 的後邊(後3/4),才會被移動到 LRU連結串列 頭部,這樣就 可以降低調整 LRU連結串列 的頻率,從而提升效能

Buffer Pool是可以有多個的,新增頁面時根據雜湊決定新增到哪個Buffer Pool。

Checkpoint

checkpoint技術是為了解決一下幾個問題:

  • 縮短資料庫恢復時間
  • 緩衝池不夠用時,將髒頁重新整理到磁碟
  • 重做日誌不可用時,重新整理髒頁

事務提交是先寫redolog,再修改頁。

checkpoint分為sharp checkpoint和fuzzy checkpoint。

當資料庫關閉時,會發生sharp checkpoint,將所有髒頁都重新整理到磁碟。其他情況使用fuzzy checkpoint。

  1. Master 執行緒會每隔一段時間從緩衝池的flush連結串列非同步重新整理一定比例髒頁回磁碟
  2. 當LRU連結串列的空閒頁不夠時,page clearner執行緒會將LRU連結串列尾端的頁移除,如果這些頁裡有髒頁,會發生checkpoint。
  3. 重做日誌不可用時,會非同步從flush連結串列中選取一些頁,強制刷回磁碟。
  4. 髒頁數量太多,達到75%時也會強制刷盤

redolog刷盤策略有三種:

  • 引數為0:表示每次事務提交時不進行刷盤操作。(系統預設master thread每隔1s進行一次重做日 志的同步)
  • 引數為1:表示每次事務提交時都將進行同步,刷盤操作( 預設值 )
  • 引數為2:表示每次事務提交時都只把 redo log buffer 內容寫入 page cache,不進行同步。由os自 己決定什麼時候同步到磁碟檔案。

redolog是在事務提交之前就在寫的,事務提交時,會根據設定的值進行刷盤。

從redo log buffer到page cache的操作是後臺執行緒每隔1s做的。

redolog files:

日誌檔案最大有100個,預設有兩個(ib_logfile 0,ib_logfile 1)。採用迴圈使用的方式向redo日誌檔案組裡寫資料的話,會導致後寫入的redo日誌覆蓋掉前邊寫的redo日 志?當然!所以InnoDB的設計者提出了checkpoint的概念。

如果 write pos 追上 checkpoint ,表示日誌檔案組滿了,這時候不能再寫入新的 redo log記錄,MySQL 得 停下來,清空一些記錄,把 checkpoint 推進一下。