我們知道InnoDB採用Write Ahead Log策略來防止宕機數據丟失,即事務提交時,先寫重做日誌,再修改內存數據頁,這樣就產生了髒頁。既然有重做日誌保證數據持久性,查詢時也可以直接從緩衝池頁中取數據,那爲什麼還要刷新髒頁到磁盤呢?如果重做日誌可以無限增大,同時緩衝池足夠大,能夠緩存所有數據,那麼是不需要將緩衝池中的髒頁刷新到磁盤。但是,通常會有以下幾個問題:
- 服務器內存有限,緩衝池不夠用,無法緩存全部數據
- 重做日誌無限增大成本要求太高
- 宕機時如果重做全部日誌恢復時間過長
事實上,當數據庫宕機時,數據庫不需要重做所有的日誌,只需要執行上次刷入點之後的日誌。這個點就叫做Checkpoint,它解決了以上的問題:
- 縮短數據庫恢復時間
- 緩衝池不夠用時,將髒頁刷新到磁盤
- 重做日誌不可用時,刷新髒頁
重做日誌被設計成可循環使用,當日志文件寫滿時,重做日誌中對應數據已經被刷新到磁盤的那部分不再需要的日誌可以被覆蓋重用。
InnoDB引擎通過LSN(Log Sequence Number)來標記版本,LSN是日誌空間中每條日誌的結束點,用字節偏移量來表示。每個page有LSN,redo log也有LSN,Checkpoint也有LSN。可以通過命令show engine innodb status
來觀察:
--- LOG --- Log sequence number 11102619599 Log flushed up to 11102618636 Last checkpoint at 11102606319 0 pending log writes, 0 pending chkp writes 15416290 log i/o's done, 12.32 log i/o's/second
Checkpoint機制每次刷新多少頁,從哪裏取髒頁,什麼時間觸發刷新?這些都是很複雜的。有兩種Checkpoint,分別爲:
- Sharp Checkpoint
- Fuzzy Checkpoint
Sharp Checkpoint發生在關閉數據庫時,將所有髒頁刷回磁盤。在運行時使用Fuzzy Checkpoint進行部分髒頁的刷新。部分髒頁刷新有以下幾種:
- Master Thread Checkpoint
- FLUSH_LRU_LIST Checkpoint
- Async/Sync Flush Checkpoint
- Dirty Page too much Checkpoint
Master Thread Checkpoint
Master Thread以每秒或每十秒的速度從緩衝池的髒頁列表中刷新一定比例的頁回磁盤。這個過程是異步的,不會阻塞查詢線程。
FLUSH_LRU_LIST Checkpoint
InnoDB要保證LRU列表中有100左右空閒頁可使用。在InnoDB1.1.X版本前,要檢查LRU中是否有足夠的頁用於用戶查詢操作線程,如果沒有,會將LRU列表尾端的頁淘汰,如果被淘汰的頁中有髒頁,會強制執行Checkpoint刷回髒頁數據到磁盤,顯然這會阻塞用戶查詢線程。從InnoDB1.2.X版本開始,這個檢查放到單獨的Page Cleaner Thread中進行,並且用戶可以通過innodb_lru_scan_depth
控制LRU列表中可用頁的數量,默認值爲1024。
Async/Sync Flush Checkpoint
是指重做日誌文件不可用時,需要強制將髒頁列表中的一些頁刷新回磁盤。這可以保證重做日誌文件可循環使用。在InnoDB1.2.X版本之前,Async Flush Checkpoint會阻塞發現問題的用戶查詢線程,Sync Flush Checkpoint會阻塞所有查詢線程。InnoDB1.2.X之後放到單獨的Page Cleaner Thread。
Dirty Page too much Checkpoint
髒頁數量太多時,InnoDB引擎會強制進行Checkpoint。目的還是爲了保證緩衝池中有足夠可用的空閒頁。其可以通過參數innodb_max_dirty_pages_pct
來設置:
mysql> show variables like 'innodb_max_dirty_pages_pct'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_max_dirty_pages_pct | 90 | +----------------------------+-------+