深入淺出InnoDB MLOG CHECKPOINT
- 2019 年 10 月 4 日
- 筆記
提示:公眾號展示程式碼會自動折行,建議橫屏閱讀
1 MLOG CHECKPOINT是什麼
在MySQL 5.7存儲引擎InnoDB崩潰恢復中,我們一定看到過MLOG_CHECKPOIN的身影。從上一個檢查點(LOG CHECKPOINT)開始,進行第一次redo日誌掃描(參考函數recv_group_scan_log_recs() ),就是要找到MLOG_CHECKPOINT。那麼MLOG_CHECKPOINT是用來做什麼的?
大家都知道在InnoDB恢復的過程,是先應用redo日誌,再執行undo操作。Redo日誌從日誌文件中讀取之後,會按照(space_id, page_no)存入到哈希表中,然後再批量地把日誌應用到數據頁中。而數據頁的讀取需要根據space_id來打開對應的數據文件。為此我們寫入如下以MLOG_FILE開頭的類型日誌來記錄文件操作:
1) MLOG_FILE_CREATE2:文件創建時寫入,格式為(type, space_id, first_page_no, flags, path)
2) MLOG_FILE_RENAME2:重命名文件時寫入,格式為(type, space_id, first_page_no, path, newpath)
3) MLOG_FILED_ELETE:文件刪除時寫入,格式為(type, space_id, first_page_no, path)
4) MLOG_FILE_NAME(MFN):文件在上一個檢查點之後第一次被修改時寫入,格式為(type, space_id, first_page_no, path)。Space初始的max_lsn為0,在mtr開始的時候,多了一次函數調用:set_named_space()。在mtr提交的時候,檢查如果max_lsn為0,則表示該表第一次被修改,寫入MLOG_FILE_NAME日誌。同時,修改max_lsn為當前日誌系統的lsn。
我們有了這些對應的日誌記錄,因此在恢復時,只需要掃描一遍日誌文件,就可以建立起space id到文件路徑的的映射。這裡存在一個問題,當系統做檢查點的時候,並不能保證,所有修改的數據頁對應的MLOG_FILE_NAME日誌記錄在檢查點之後都存在。也就是說檢查點可能正好位於某個數據文件對應的MLOG_FILE_NAME日誌之後,在該數據文件對應的數據頁修改日誌記錄之前。

如上圖所示,檢查點-1之後,space_id為100第一次修改,記錄了MFN(MLOG_FILE_NAME)日誌。新生成的檢查點-2之後,有該文件的頁面修改日誌。如果系統恢復從檢查點-2開始,則沒有MLOG_FILE_NAME記錄,我們就無從得到該數據文件對應的文件路徑,對應的數據頁日誌記錄就無法正常應用。
為了彌補這種缺失MLOG_FILE_NAME記錄的情況,MLOG_CHECKPOINT應運而生。其基本原理是補充寫入MLOG_FILE_NAME,並記錄MLOG_CHECKPOINT標記。如下圖所示:

在系統做檢查點時(參見函數log_checkpoint()),對當前活動的所有數據文件做一個檢查點(參見函數fil_names_clear())。掃描所有記憶體中的space,如果max_lsn大於檢查點lsn,那麼再次寫入對應的MLOG_FILE_NAME日誌,並重置max_lsn為0。當這些日誌寫完之後,寫入MLOG_CHECKPOINT,即表示補充的MLOG_FILE_NAME日誌結束。因此當系統恢復時,掃描碰到MLOG_CHECKPOINT日誌,則意味著,在它之前,檢查點之後的所有日誌對應的space映射已經完全建立起來了。在它之後,就不存在MLOG_FILE_NAME記錄缺失的情況了。
當映射關係建立起來之後,應用日誌之前,要對存在哈希表中的日誌記錄進行預處理(參見函數recv_init_crash_recovery_spaces())。對於文件是刪除狀態的日誌記錄,則可以丟棄;對於文件是缺失狀態的日誌記錄,則可以告警或者報錯。
這裡還有一點需要指出,從檢查點開始日誌掃描,有可能會掃描到不止一條MLOG_CHECKPOINT日誌。每條MLOG_CHECKPOINT日誌都記錄了對應的檢查點LSN,因此必然能找到起始檢查點對應的MLOG_CHECKPOINT日誌(參見函數recv_parse_log_recs()和變數recv_sys->mlog_checkpoint_lsn),在它之後,最多可能存在一條MLOG_CHECKPOINT日誌,即為下一個未完成的檢查點寫入的。

上圖中,如果從檢查點-2開始恢復,MLOG_CHECKPOINT-2和其對應的MLOG_FILE_NAME日誌是恢復所必須的。MLOG_CHECKPOINT-1和MLOG_CHECKPOINT-3則不是,但也沒有副作用。檢查點和MLOG_CHECKPOINT之間相互交錯,極大地增加了系統恢復的複雜度。
2 MLOG CHECKPOINT的前世今生
在5.6中,是通過掃描所有數據文件,讀取第一個數據頁獲取ID,以此建立space和文件路徑的映射關係。
在5.7中,MLOG_CHECKPOINT是在WL#7142: InnoDB: Simplify tablespace discovery during crash recovery中引入。相對於5.6的方案,好處是,不用掃描額外的數據文件(比如孤兒文件等),另外對於沒有對應數據文件的日誌是否應該丟棄更準確。但是新的方案也引入了新的問題,比如系統檢查點邏輯和恢復邏輯變得更複雜,導致了很多新的bug。系統恢復時重複掃描,也導致恢復時間變長。後一個問題其實可以通過將MLOG_CHECKPOINT寫入到單獨的日誌文件中解決。
為了解決這些問題,8.0拋棄了MLOG_CHECKPOINT解決方案。WL#9499 – InnoDB: Replace MLOG_FILE_NAME with MLOG_FILE_OPEN。在新的解決方案中,space到文件路徑的映射關係寫入到了兩個文件tablespaces.open.1和tablespaces.open.2中,同時支援–innodb-scan-directories進行數據文件掃描(如5.6的方式)。新增的兩個文件也類似於記錄補償的文件映射補償日誌。筆者當時作為WL#9499的程式碼審查人之一,深切感受到要把這個看似簡單的功能寫的穩定可靠,的確要耗費不少心血。
那麼除了以上三種方案之外,是否還有其他選擇?當然有。通過InnoDB數據字典里space和datafile系統表,就可以建立space到文件的完整映射關係。但是這種方案更加複雜:我們需要在系統恢復之前,先恢複數據字典表。為了先恢復字典表,則需要先掃描日誌文件,獲取數據字典表的日誌資訊,優先應用,然後還要進行相應的undo操作。由於字典表的undo記錄可能和數據的undo記錄在同一事務中,又給字典表單獨undo增加了難度。不管是從性能和複雜度來說,都未必比前面三種更優。
綜上所述,MLOG_CHECKPOINT已經完成了其在MySQL版本迭代中的歷史使命。相比較三種解決方案,5.6的解決方案更簡單更可靠。8.0則對5.6和5.7的解決方案進行了綜合,也不失為一個好的選擇。
參考文檔:WL#7142: InnoDB: Simplify tablespace discovery during crash recovery

騰訊資料庫技術團隊對內支援QQ空間、微信紅包、騰訊廣告、騰訊音樂、騰訊新聞等公司自研業務,對外在騰訊雲上支援TencentDB相關產品,如CynosDB、CDB、CTSDB、CMongo等。騰訊資料庫技術團隊專註於持續優化資料庫內核和架構能力,提升資料庫性能和穩定性,為騰訊自研業務和騰訊雲客戶提供「省心、放心」的資料庫服務。此公眾號和廣大資料庫技術愛好者一起,推廣和分享資料庫領域專業知識,希望對大家有所幫助