MySQL 表資料多久刷一次盤?

detectiveHLH發表於2022-04-07

前言

事情是這樣的,在某乎的邀請回答中看到了這個問題:

-

然後當時我沒多想就啪一下寫下來這樣的答案:

這個其實要通過 MySQL 後臺執行緒來刷的,在 Buffer Pool 中被修改的過的 Page(頁)都會被標記成髒頁,放到一個連結串列(Flush 連結串列)裡。

然後 MySQL 通過啟動後臺執行緒,在滿足條件時將 Flush 連結串列中的髒頁刷入磁碟。

滿足的條件是:髒頁的數量達到了 Buffer Pool 中頁數量的 **10%,當然 10% 這個值是可變的,通過配置項 innodb_max_dirty_pages_pct_lwm 來配置的,其預設值為 10%,並且這個值也必須小於另一個配置 innodb_max_dirty_pages_pct 的值(90%**)。

至於啟多少個執行緒,則是由另一個變數 innodb_page_cleaners 來控制的,預設是 4.一般都不會去改這個。

大概就是這樣。

但是,後面有兄弟在下面說:”我唔知你喺講乜“。

後面我回過頭去看,當時寫的確實有點過於跳躍了,過一段時間再去看有些不是那麼連貫,打算重新把這個事情講清楚。

1. 表資料

我們這篇「短文」討論的是【MySQL 表資料多久刷一次盤】,從這個標題中我們可以分裂成兩個問題:

  1. 刷什麼到磁碟
  2. 什麼時候刷到磁碟

我們分開來討論。

2. 刷什麼到磁碟

看上去有點廢話,肯定是將資料刷入磁碟。所以我們更多需要討論的是【資料是以什麼樣的形式被刷入磁碟】。

答案是頁

對頁不太瞭解的可以去看看之前寫的文章:MySQL 頁完全指南——淺入深出頁的原理

在 InnoDB 中,是資料被管理的最小的單位。當使用 InnoDB 作為儲存引擎的 MySQL 執行時,表中一行一行的資料會被組織在一頁一頁當中,放在 Buffer Pool 中。

Buffer Pool 可以看另一篇:詳細瞭解 InnoDB 記憶體結構及其原理

這一頁一頁的資料,就存放在 Buffer Pool 中。當 DML 語句(也就是 CRUD)語句對錶資料進行了變更之後,資料所在的那一頁就會被標記為髒頁

InnoDB 會用一個叫【Flush 連結串列】的結構來存放這些髒頁,凡是被放進該連結串列的頁都代表需要刷入磁碟,但不是立即刷入。

和 InnoDB 的其他日誌例如 Redo Log 一樣,這些日誌都是有自己的刷盤策略。例如 Redo Log,其刷盤策略可以用下圖來表示:

引數為0,Redo Log 會每隔一秒,寫入並且刷入磁碟。

引數為1,Redo Log 會在每次事務提交之後刷入磁碟

引數為2,每次事務提交,都會寫到 OS 快取中去,然後每隔一秒將 OS 快取中的資料刷入磁碟

而 Flush 連結串列也有自己的策略。

3. 什麼時候刷到磁碟

接上節,策略就是:髒頁的數量達到了 Buffer Pool 中頁數量的 **10%**,就會觸發將 Flush 連結串列中的髒頁刷入磁碟。舉個例子,Buffer Pool 中總共有 100 張頁,髒頁如果達到了 10 頁就會啟動後臺執行緒,觸發刷盤。

當然,【10%】這個數值是可配置的,通過 MySQL 配置項 innodb_max_dirty_pages_pct_lwm 可以進行調整,只是預設值是 10%。但是我們調整的值不能超過某個最大值,這個最大值由 innodb_max_dirty_pages_pct 來指定,預設值為 90%。

換句話說,預設情況,刷盤閾值是 10%,如果需要自定義,則最大值不能超過 90%。

4. 誰來負責刷盤

上個小節已經說過了,會啟動執行緒來專門做這個事情,這個沒有什麼疑問。我們需要關注的是會啟動多少個執行緒來做這個事。

答案是 4 個,我們也可以通過配置項 innodb_page_cleaners 來更改,但一般都不會去改這個值。

關於這個點就聊到這。

歡迎微信搜尋關注【SH的全棧筆記】,如果你覺得這篇文章對你有幫助,還麻煩點個贊關個注分個享留個言

相關文章