mysql 是如何保證 crash-safe 的

關於這個問題,我想你面試的時候會經常被問到,死記硬背是不行的,因爲這個問題可以延展出更多的問題來,因此必須真正理解了纔行。

所謂的 crash-safe,也就是說再遇到極端情況,比如機器重啓,數據仍然不丟失。

我相信你肯定遇到過這種情況,在寫 word 的過程中,電腦突然卡死,或者 word 突然崩潰,然後再打開時發現自己辛辛苦苦碼的文字已經消失不見了。這就不是 crash-safe。

遇到這種情況怎麼解決呢? 假如 word 沒 1 秒鐘自動保存一次,那麼出現電腦突然卡死,或者 word 突然崩潰的情況時,最多損失 1 秒內編輯的文字,就解決了這個問題。

mysql 的邏輯跟這個差不多,但是要更復雜一些。

mysql 的存儲引擎基本上都用 InnoDB,因爲它支持事務,以這個引擎爲例說明:

InnoDB 有個日誌文件叫重做日誌,redo log,就可以持久化存在磁盤上的,但是在內存中也有一份對應的緩衝區,叫 redo log buffer,爲了應對異常重啓,InnoDB 有一個後臺線程,每隔 1 秒,就會把 redo log buffer 中的日誌,調用 write 寫到文件系統的 page cache,然後調用 fsync 持久化到磁盤。

也就是 redo log buffer -> page cache -> 磁盤 這一過程,每秒都在進行,一旦發生異常重啓,從 redo log 中恢復就可以了。那具體是怎麼恢復的呢?

事務提交之前,先寫入 redo log,狀態是 prepare,表示已經準備好了,隨時可以提交。

事務提交之後,redo log 對應的狀態是 commit,表示已經提交。

如果是 prepare 時發生異常重啓,mysql 在恢復後對狀態爲 prepare 狀態的事務進行回滾。

如果是 commit 狀態,表示本來已經寫完了,重啓也沒關係。

如果是 prepare 之前崩潰了,也無所謂,本來就沒有開始寫數據,重啓也沒有任何損失。

現在有了 redo log,只能保證數據不丟,但還無法保證數據可以恢復到之前的某一時刻的狀態。這就需要 binlog,binlog 是 mysql 自帶的歸檔日誌。

假如在寫 binlog 前異常重啓,mysql 在恢復後對狀態爲 prepare 狀態的事務進行回滾。

假如在寫 binlog 後異常重啓,則判斷對應的事務 binlog 是否存在並完整:

  • a. 如果是,則提交事務;
  • b. 否則,回滾事務。

你可能會問,處於 prepare 階段的 redo log 加上完整 binlog,重啓就能恢復,MySQL 爲什麼要這麼設計?

回答: binlog 寫完以後 MySQL 發生崩潰,這時候 binlog 已經寫入了,之後就會被從庫(或者用這個 binlog 恢復出來的庫)使用。所以,在主庫上也要提交這個事務。採用這個策略,主庫和備庫的數據就保證了一致性。

還有一個問題,就是爲什麼不讓 redo log 也承擔 binlog 的功能?

這是因爲,redo log 是循環寫的,寫完後會從開頭繼續寫,這樣 redo log 就無法記錄一段時間內的完整操作,這樣歷史日誌沒法保留,redo log 也就起不到歸檔的作用。

另一個原因就是就是 MySQL 系統依賴於 binlog。binlog 作爲 MySQL 一開始就有的功能,被用在了很多地方。其中,MySQL 系統高可用的基礎,就是 binlog 複製。還有很多公司有異構系統(比如一些數據分析系統),這些系統就靠消費 MySQL 的 binlog 來更新自己的數據。關掉 binlog 的話,這些下游系統就沒法輸入了。

如有問題歡迎留言討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章