此文為極客時間:MySQL實戰45講的12節的學習筆記
一、mysql 的刷盤機制
而之前提到過,mysql 使用了 WAL 技術,即更新的時候先更新內存中的數據,然后必要的時候再將內存中的數據刷入磁盤。我們把內存中這些被修改過,跟磁盤中的數據頁不一致的數據頁稱為臟頁。
其中,有四種情況會觸發臟頁的刷盤:
- redo log 可寫空間滿了。
- 內存滿了,需要淘汰的數據頁恰好是臟頁。
- 系統不繁忙的時候。
- 關閉數據庫的時候。
其中,第三種情況不會為系統帶來過多影響的,第四中情況下不會在乎為系統帶來的影響。所以我們只需要關注第一和第二種情況:
對於第二種情況,由於 mysql 的更新需要先寫日志,所以當日志滿了的情況下,所有的更新都會停止,一直到刷完盤日志騰出了空間為止;
而對於第二種情況,當查詢的數據在內存中的數據頁沒有的時候,就需要淘汰舊頁釋放內存以讀入新頁,所以當一次查詢導致需要淘汰的臟頁過多的時候,就需要先等待較長的刷盤時間,然后才能獲取響應。
為了避免上述兩種情況,必須要控制臟頁在內存中的比例。
二、刷臟頁的控制策略
首先,我們必須要知道主機磁盤的寫入能力有多強,這樣 innodb 才可以知道它刷臟頁的速度最快應該是多快。
我們可以通過設置 innodb_io_capacity
這個參數來告訴 innodb 磁盤的寫入速度。這個參數的值不宜過小,因為這會導致 innodb 錯誤的估計刷盤速度,最后導致刷臟頁的速度跟不上臟頁生成的速度。
innodb_io_capacity
規定了刷臟頁速度的極限,但是實際上磁盤不可能只服務這么一個功能,所以還需要參考 redo log 的刷盤速度和允許的內存中的臟頁比例。
參數 innodb_max_dirty_pages_pct
是臟頁比例上限,默認值是 75%。innodb 會根據當前的臟頁比例(假設為 M),算出一個范圍在 0 到 100 之間的數字,這個公式是 F1(M)
。
而每次寫入 redo log 的寫入點 wp 都會有一個序號,innodb 會根據這個序號和上一次清理日志的界限 cp 之間的差值——我們假設為 N——計算得到一個范圍在 0 到 100 之間的數字,這個公式是 F2(N)
根據上述算得的 F1(M)
和 F2(N)
兩個值,取其中較大的值記為 R,之后引擎就可以按照 innodb_io_capacity
定義的能力乘以 R% 來控制刷臟頁的速度。
這一整個流程對應的圖片是這樣的:
所以,我們需要關注內存中的臟頁比例,讓它盡量不要到75%,並且合理的設置 innodb_io_capacity
參數。
其中,針對臟頁的比例,我們可以通過 Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total
去設置。
另外,由於 mysql 存在這樣一個機制:如果要刷盤的臟頁相鄰的數據頁恰好也是臟頁,就一起寫入磁盤,如果鄰居的鄰居也是如此。在機械硬盤時代這個策略可以減少隨機IO,但是如果使用固態硬盤的話隨機IO的性能往往比較高,所以使用這個策略反而拖累了查詢性能。因此可以通過 innodb_flush_neighbors
關閉這個“連坐”的策略。
三、總結
innodb 有四種情況會觸發臟頁的刷盤:
- redo log 可寫空間滿了;
- 內存滿了,需要淘汰的數據頁恰好是臟頁;
- 系統不繁忙的時候;
- 關閉數據庫的時候。
innodb 通過的刷盤速度通過類似這樣的公式計算:
innodb_io_capacity * Max( F(innodb_max_dirty_pages_pct), F(redo log的wp - redo log的cp) )
其中,
innodb_io_capacity
表示磁盤的最大IO能力;innodb_max_dirty_pages_pct
表示允許臟頁在內存中的占比,默認值為75%;
當使用固態硬盤的時候,可以設置innodb_flush_neighbors
關閉默認的刷新相鄰臟頁的策略。