您好,登錄后才能下訂單哦!
小編給大家分享一下MySQL中常見的日志問題有哪些,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
MySQL 里有兩個日志,即:重做日志(redo log)和歸檔日志(binlog)。
其中,binlog 可以給備庫使用,也可以保存起來用于恢復數據庫歷史數據。它是實現在 server 層的,所有引擎可以共用。redo log 是 InnoDB 特有的日志,用來支持 crash-safe 能力。
你一定聽過 MySQL 事務的兩階段提交,指的就是在事務提交的時候,分成 prepare 和 commit 兩個階段。
如圖 1 所示為一個事務的執行流程,你在最后三步可以看到,redo log 先 prepare 完成,再寫 binlog,最后才進入 redo log commit 階段。
圖 1 兩階段提交示意圖
這里,我要先和你解釋一個誤會式的問題:這個圖不就是一個 update 語句的執行流程嗎,怎么還會調用 commit 語句?
通常情況下,你會產生這個疑問的原因,在于把兩個“commit”的概念混淆了:
問題中的“commit 語句”,是指 MySQL 語法中,用于提交一個事務的命令。一般跟 begin/start transaction 配對使用。
而我們圖中用到的這個“commit 步驟”,指的是事務提交過程中的一個小步驟,也是最后一步。當這個步驟執行完成后,這個事務就提交完成了。
“commit 語句”執行的時候,會包含“commit 步驟”。
而我們這個例子里面,沒有顯式地開啟事務,因此這個 update 語句自己就是一個事務,在執行完成后提交事務時,就會用到這個“commit 步驟”。
接下來,我們就一起分析一下在兩階段提交的不同時刻,MySQL 異常重啟會出現什么現象。
如果在圖中時刻 A 的地方,也就是寫入 redo log 處于 prepare 階段之后、寫 binlog 之前,發生了崩潰(crash),由于此時 binlog 還沒寫,redo log 也還沒提交,所以崩潰恢復的時候,這個事務會回滾。這時候,binlog 還沒寫,所以也不會傳到備庫。到這里,我們都可以理解。
而我們理解會出現問題的地方,主要集中在時刻 B,也就是 binlog 寫完,redo log 還沒 commit 前發生 crash,那崩潰恢復的時候 MySQL 會怎么處理?
我們先來看一下崩潰恢復時的判斷規則。
1、如果 redo log 里面的事務是完整的,也就是已經有了 commit 標識,則直接提交;
2、如果 redo log 里面的事務只有完整的 prepare,則判斷對應的事務 binlog 是否存在并完整:
a.如果是,則提交事務;
b.否則,回滾事務。
這里,時刻 B 發生 crash 對應的就是 2(a) 的情況,崩潰恢復過程中事務會被提交。
現在,我們就針對兩階段提交再繼續延展一下。
回答:一個事務的 binlog 是有完整格式的:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而沒有 commit 的 redo log,就拿著 XID 去 binlog 找對應的事務。
問題 3:處于 prepare 階段的 redo log 加上完整 binlog,重啟就能恢復,MySQL 為什么要這么設計?
回答:其實,這個問題還是跟我們在反證法中說到的數據與備份的一致性有關。在時刻 B,也就是 binlog 寫完以后 MySQL 發生崩潰,這時候 binlog 已經寫入了,之后就會被從庫(或者用這個 binlog 恢復出來的庫)使用。
所以,在主庫上也要提交這個事務。采用這個策略,主庫和備庫的數據就保證了一致性。
問題 4:如果這樣的話,為什么還要兩階段提交呢?干脆先 redo log 寫完,再寫 binlog。崩潰恢復的時候,必須得兩個日志都完整才可以。是不是一樣的邏輯?
回答:其實,兩階段提交是經典的分布式系統問題,并不是 MySQL 獨有的。
如果必須要舉一個場景,來說明這么做的必要性的話,那就是事務的持久性問題。
對于 InnoDB 引擎來說,如果 redo log 提交完成了,事務就不能回滾(如果這還允許回滾,就可能覆蓋掉別的事務的更新)。而如果 redo log 直接提交,然后 binlog 寫入的時候失敗,InnoDB 又回滾不了,數據和 binlog 日志又不一致了。
兩階段提交就是為了給所有人一個機會,當每個人都說“我 ok”的時候,再一起提交。
問題 5:不引入兩個日志,也就沒有兩階段提交的必要了。只用 binlog 來支持崩潰恢復,又能支持歸檔,不就可以了?
回答:我把這個問題再翻譯一下的話,是說只保留 binlog,然后可以把提交流程改成這樣:... -> “數據更新到內存” -> “寫 binlog” -> “提交事務”,是不是也可以提供崩潰恢復的能力?
答案是不可以。
如果說歷史原因的話,那就是 InnoDB 并不是 MySQL 的原生存儲引擎。MySQL 的原生引擎是 MyISAM,設計之初就有沒有支持崩潰恢復。
InnoDB 在作為 MySQL 的插件加入 MySQL 引擎家族之前,就已經是一個提供了崩潰恢復和事務支持的引擎了。
InnoDB 接入了 MySQL 后,發現既然 binlog 沒有崩潰恢復的能力,那就用 InnoDB 原有的 redo log 好了。
而如果說實現上的原因的話,就有很多了。就按照問題中說的,只用 binlog 來實現崩潰恢復的流程,我畫了一張示意圖,這里就沒有 redo log 了。
圖 2 只用 binlog 支持崩潰恢復
這樣的流程下,binlog 還是不能支持崩潰恢復的。我說一個不支持的點吧:binlog 沒有能力恢復“數據頁”。
如果在圖中標的位置,也就是 binlog2 寫完了,但是整個事務還沒有 commit 的時候,MySQL 發生了 crash。
重啟后,引擎內部事務 2 會回滾,然后應用 binlog2 可以補回來;但是對于事務 1 來說,系統已經認為提交完成了,不會再應用一次 binlog1。
但是,InnoDB 引擎使用的是 WAL 技術,執行事務的時候,寫完內存和日志,事務就算完成了。如果之后崩潰,要依賴于日志來恢復數據頁。
也就是說在圖中這個位置發生崩潰的話,事務 1 也是可能丟失了的,而且是數據頁級的丟失。此時,binlog 里面并沒有記錄數據頁的更新細節,是補不回來的。
你如果要說,那我優化一下 binlog 的內容,讓它來記錄數據頁的更改可以嗎?可以,但這其實就是又做了一個 redo log 出來。
所以,至少現在的 binlog 能力,還不能支持崩潰恢復。
問題 6:那能不能反過來,只用 redo log,不要 binlog?
回答:如果只從崩潰恢復的角度來講是可以的。你可以把 binlog 關掉,這樣就沒有兩階段提交了,但系統依然是 crash-safe 的。
但是,如果你了解一下業界各個公司的使用場景的話,就會發現在正式的生產庫上,binlog 都是開著的。因為 binlog 有著 redo log 無法替代的功能。
一個是歸檔。redo log 是循環寫,寫到末尾是要回到開頭繼續寫的。這樣歷史日志沒法保留,redo log 也就起不到歸檔的作用。
一個就是 MySQL 系統依賴于 binlog。binlog 作為 MySQL 一開始就有的功能,被用在了很多地方。其中,MySQL 系統高可用的基礎,就是 binlog 復制。
還有很多公司有異構系統(比如一些數據分析系統),這些系統就靠消費 MySQL 的 binlog 來更新自己的數據。關掉 binlog 的話,這些下游系統就沒法輸入了。
總之,由于現在包括 MySQL 高可用在內的很多系統機制都依賴于 binlog,所以“鳩占鵲巢” redo log 還做不到。你看,發展生態是多么重要。
最后,推薦你關注丁奇的《MySQL 實戰 45 講》專欄。在專欄里,丁奇會幫你梳理出學習 MySQL 的主線知識,比如事務、索引、鎖等,還會就開發過程中經常遇到的具體問題和你分析討論,并且幫你理解問題背后的本質。你會收獲 MySQL 核心技術詳解與原理說明和 36 個 MySQL 常見痛點問題解析。
statement 格式的 binlog,最后會有 COMMIT;
row 格式的 binlog,最后會有一個 XID event。
另外,在 MySQL 5.6.2 版本以后,還引入了 binlog-checksum 參數,用來驗證 binlog 內容的正確性。對于 binlog 日志由于磁盤原因,可能會在日志中間出錯的情況,MySQL 可以通過校驗 checksum 的結果來發現。所以,MySQL 還是有辦法驗證事務 binlog 的完整性的。
回答:它們有一個共同的數據字段,叫 XID。崩潰恢復的時候,會按順序掃描 redo log:
看完了這篇文章,相信你對MySQL中常見的日志問題有哪些有了一定的了解,想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。