MySQL 15 日志相關問題追問
先放一下兩階段提交的圖,在后續問題中會用到:

問題
在MySQL 02中,講到為什么要使用兩階段提交時用的是反證法,說明了如果不使用兩階段提交,會導致MySQL出現主備數據不一致等問題。
那么如果在兩階段提交的不同瞬間,MySQL如果發生異常重啟,是怎么保證數據完整性的呢?
如果在圖中時刻A,也就是寫入redo log后、寫binlog前發生了崩潰,由于此時binlog還沒寫,redo log也還沒提交,所以崩潰恢復的時候,該事務會回滾。因為binlog還沒寫,也不會傳到備庫。
如果在圖中時刻B,也就是binlog寫完,redo log還沒commit前發送崩潰,會怎么樣呢?
先看一下崩潰恢復時的判斷規則:
-
如果redo log里的事務是完整的,也就是已經有commit標識,則直接提交;
-
如果redo log里的事務只有完整的prepare,則判斷對應的事務binlog是否存在并完整:
-
是,則提交事務;
-
否,回滾事務。
-
因此,時刻B崩潰恢復過程中事務會被提交。
追問1:MySQL怎么知道binlog是完整的?
一個事務的binlog有完整的格式:
-
statement格式的binlog,最后會有COMMIT語句;
-
row格式的binlog,最后會有一個XID event作為標識。
在MySQL 5.6.2 版本后,還引入了binlog-checksum參數,用來驗證binlog內容的正確性。對于binlog日志由于磁盤原因可能在日志中間出錯的情況,MySQL可以通過校驗該參數的結果來發現。
追問2:redo log和binlog是怎么關聯起來的?
兩者有一個共同的數據字段XID。崩潰恢復的時候,會按順序掃描redo log:
-
如果碰到既有prepare,又有commit的redo log,就直接提交;
-
如果碰到只有prepare,而沒有commit的redo log,就拿著XID去binlog找對應的事務。
追問3:為什么設計為,prepare的redo log+完整binlog,重啟就能恢復?
這個問題也與數據與備份的一致性有關。在時刻B,binlog已經寫完,之后會被從庫使用,因此在主庫上也要提交這個事務,才能做到一致性。
追問4:如果這樣為什么還要兩階段提交?為什么不先把redo log寫完,再寫binlog。而等崩潰恢復要求兩個日志都完整?
兩階段提交是經典的分布式系統問題,并不是MySQL獨有的。如果必須要說明這樣設計的原因,那就是事務的持久性問題。
對于InnoDB來說,如果redo log提交完成,事務就不能回滾。而如果redo log直接提交,然后binlog寫入失敗,InnoDB又無法回滾,那么數據和binlog又不一致了。
追問5:只用binlog來支持崩潰恢復可以嗎?
即把流程改為:… -> 數據更新到內存 -> 寫binlog -> 提交事務。
答案是不可以的。
從歷史原因說,InnoDB不是MySQL的原生存儲引擎,而MyISAM設計之初就沒有支持崩潰恢復。
從實現上說,如果只用binlog:

如圖,假如在binlog2寫完但整個事務還沒有commit時,MySQL發生crash,重啟后引擎內部事務2會回滾,但對于事務1來說,系統認為已經提交完成,不會再應用一次binlog1。
如果InnoDB使用的是WAL技術,執行事務的時候,寫完內存和日志,事務就算完成。如果之后崩潰,要依賴日志來恢復數據頁。那么這種情況下,由于不應用binlog1,事務1也可能丟失,而且是數據頁級別的丟失。此時,binlog里沒有記錄數據頁的更新細節,是補不回來的。
追問6:那能只用redo log,不要binlog嗎?
如果只從崩潰恢復的角度來說是可以的。
使用binlog主要是它有著redo log無法替代的功能:
-
歸檔。redo log是循環寫,歷史日志無法保留,起不到歸檔的作用。
-
MySQL高可用的基礎就是binlog復制。
-
很多公司有異構系統,這些系統靠消費MySQL的binlog來更新自己的數據。關掉binlog的話,這些下游系統就沒法輸入。
追問7:redo log一般設置多大?
如果redo log太小,會導致很快寫滿。
對于幾個TB的磁盤,一般將redo log設置為4個文件,每個文件1GB。
追問8:正常運行的實例,數據寫入后的最終落盤,是從redo log更新過來的還是從buffer pool更新過來的?
redo log并沒有記錄數據頁的完整數據,所以它并沒有能力自己去更新磁盤數據頁,也就不存在“數據最終落盤,是由redo log更新過去”的情況。
-
如果是正常運行的實例,數據頁被修改以后,跟磁盤的數據頁不一致,稱為臟頁。最終數據落盤,就是把內存中的數據頁寫盤;
-
在崩潰恢復場景,如果一個數據頁在崩潰恢復時丟失了更新,InnoDB會將其讀到內存,然后讓redo log更新內存內容。更新完成后,同上。
追問9:redo log buffer是什么?在寫入時,是先修改內存,還是先寫redo log文件?
redo log buffer是一塊內存,是在事務還沒commit時,先保存redo日志內容的。
真正把日志寫到redo log文件(文件名為ib_logfile+數字),是在執行commit語句時候完成的。

浙公網安備 33010602011771號