Mysql 中寫操作時保駕護航的三兄弟!

*IT界農民工*發表於2021-01-26

這期的文章主要是講述寫操作過程中涉及到的三個日誌檔案,看過前幾期的話可能你或多或少已經有些瞭解了(或者從別的地方也瞭解過)。比如整個寫操作過程中用到的兩階段提交,又或者是操作過程中涉及到的日誌檔案,但是總體來說不是很系統更談不上全面。

今天我們就來會會這三兄弟。

圖注:思維導圖

兩階段提交

這個名詞你應該聽到過很多次了,在這裡再介紹下這位老朋友。

所謂的兩階段提交,從字面意思來看應該是有兩個步驟來進行約束的。事實上也是如此。這兩個步驟中的主角就是我們今天要講的重要角色中的兩位:binlog 和 redo log

提到兩階段提交,SQL 語句的執行流程就繞不過去了。沒轍,雖然提了很多遍,但還得再拉出來溜溜。只不過這次的側重點和前面的會有些不同。

具體到操作流程上是這樣的:

當執行某個寫操作的 SQL 時,引擎將這行資料更新到記憶體的同時把對應的操作記錄到 redo log 裡面,然後處於 prepare 狀態。並把完成資訊告知給執行器。

執行器生成對應操作的 binlog,並把 binlog 寫入磁碟裡。然後呼叫引擎的提交事務介面,變更 redo log 狀態為 commit,這樣操作就算完成了。

好了,知道了兩階段提交後,我們接下來看看這些日誌檔案的真面目。 

重做日誌(redo log)

首先出場的是位於儲存引擎層的 redo log,它是用來記錄在"資料頁做了什麼修改"的物理日誌檔案。

WAL 技術

提到 redo log,WAL 技術必然是繞不過去的,全稱是 Write-Ahead Logging。也就是在同步磁碟前先寫日誌,然後系統再根據一定的策略將日誌裡的記錄同步到磁碟裡。

存在的必要性

從上邊的兩階段提交的過程裡,我們可以看到 WAL 技術的使用場景。不知道你有沒有疑惑,為什麼中間非要寫 redo log,直接將更新結果同步到磁碟裡不行嗎?傻孩子,同步到磁碟裡就意味著每次寫操作就得產生隨機寫盤操作,速度得多慢啊。

機智的你可能會說了,那我能不能一定的時間後從記憶體再同步到磁碟裡,這種方式不行嗎?來,先給你個腦瓜崩,你想想,我服務重啟了,這些資料還在不?記憶體是易失的,不知道什麼異常情況就會導致資料丟失。所以這時候就需要一個能持久化的中間檔案,起到"緩衝"的作用,並且寫入速度還不慢。

那麼 redo log 就應運而生了。雖然同樣儲存在磁碟上,但是順序寫入在速度上並不受影響(疑惑的同學可以瞭解下磁碟的隨機與順序讀寫的區別)。

當然 redo log 除了能起到"延遲"同步磁碟檔案的作用外,在資料庫伺服器當機時,還可以用來恢復資料。

寫入時機 

談到寫入時機,是不是更疑惑了,難倒不是更新完記憶體就寫入 redo log 檔案嗎?答案確實不是,因為中間還有一個 redo log buffer(記憶體中) 。Mysql 每執行一條語句,會先將記錄寫入 redo log buffer,後續執行 commit 操作時會以一定的時機寫入到 redo log 檔案(磁碟上)中。

值得注意的是,redo log buffer 裡的資料是在執行 commit 操作時寫入到 redo log 檔案中的。

至於寫入的時機,則由下面的引數來控制的: 

 

 

(圖片源自網路)

寫入方式 

知道了寫入時機,這裡簡單介紹下寫入的方式吧。在 Innodb 中,redo log 的大小是固定的,那麼就只能是以迴圈的方式進行寫入了。假如當前我有 4 個檔案,從第一個檔案開始寫入,直到最後一個檔案寫滿為止,再回到開頭將資料同步至檔案後擦除掉繼續寫。

圖中的 write pos 表示當前記錄的位置,隨著不斷寫入逐漸後移。當寫到 ib_logfile_3號檔案時,整個 redo log 就被寫滿了。此時更新操作就會被阻塞。系統根據 check point 標記位來擦除掉一些記錄(當然前提是把這些記錄同步至磁碟)。

總得來看 redo log 的寫入方式就是一個不斷寫入,寫滿後擦除,又寫入的過程。

二進位制日誌(binlog) 

說完了 redo log,我們再來看看另一個位於服務層的二進位制日誌檔案 binlog,這位大兄弟扮演的角色是儲存邏輯日誌的,所謂的邏輯日誌就是指修改了什麼,都會記錄其中。

例如:對 id = 1 的欄位進行更新操作。

當然除了記錄操作過程外,它還有支援主從同步資料異常恢復的能力。

寫入模式 

binlog 中有三種寫入模式,我們分別來看下有什麼不同及對應的優缺點:

(圖片源自網路)

寫入方式

與 redo log 迴圈寫不同的是, binlog 採用追加的方式寫入,當一個檔案寫到一定大小後就會切換到另一個。

與 redo log 的關聯

在上面的兩階段提交裡我們有提到過在寫入binlog 後會呼叫引擎的提交事務介面,變更 redo log 狀態為 commit。那麼它是如何找到對應的記錄,或者換句話說,它們兩者是怎麼關聯起來的呢?

答案是通過一個共同的欄位 XID,不僅在事務提交時,在崩潰恢復的時候如果遇到僅寫入 prepare 而沒有 commit 的 redo log,也可以通過 XID 去尋找對應的事務。

回顧下寫流程

到這裡我們有必要回顧下寫流程的操作,以更新某個欄位為例:

回滾日誌(undo log) 

到這裡,你可能會疑惑了,通篇裡哪有 undo log 的影子,你個渣男!

別急,來了!

根據字面意思,你應該能猜出來它是幹啥的。回滾嘛,也就是給你一次後悔的機會。在進行資料修改時,同時記錄 undo log,即同時記錄相反操作的邏輯日誌。你可以理解為操作 update 的時候,寫一條對應相反的 update 記錄,操作 delete 的時候,寫一條對應的 insert 記錄。 

當事務回滾時。從 undo log 中讀取到對應的邏輯記錄就可以進行回滾操作了。

總結

兩階段提交

  • 兩階段提交過程中,更新記憶體的同時把對應操作記錄到 redo log 中,並把生成的binlog 寫入磁碟後提交事務。

重做日誌 

  • redo log 是位於儲存引擎層的物理日誌,用來記錄在“資料頁做了什麼修改”的物理日誌檔案。採用迴圈寫的方式,記錄資料被修改後的樣子。同時還提供資料恢復的能力。

二進位制日誌

  • binlog是位於服務層的邏輯日誌,用來記錄“對資料做了什麼修改”的日誌檔案。與 redo log 不同的是,可以一直進行追加寫入。同時還提供主從同步及資料異常恢復的能力。

回滾日誌 

  • 在資料修改時,同時記錄 undo log,可以確保在事務回滾操作時進行資料還原。

關於作者

作者:大家好,我是萊烏,BAT搬磚工一枚。從小公司進入大廠,一路走來收穫良多,想將這些經驗分享給有需要的人,因此建立了公眾號【IT界農民工】。定時更新,希望能幫助到你。同時,我給大家肝了一份Redis面經手冊,在我的公眾號內回覆【pdf】即可獲取,希望對你有所幫助。

相關文章