Mysql三種日誌(binlog,redolog,undolog)的作用和區別

Mysql有三種很重要的日誌也是面試經常涉及到的考點,分別是 binlog 、redo log和undo log, 這裡面binlog 是server層實現的日誌,而redo log 和undo log都是引擎層(innodb)實現的日誌。也正是因為它們在Mysql不同的體系結構里,所以他們所針對的問題也是完全不同的,下面我們就來詳細講解下這三種日誌的作用以及它們之間的區別。

一、什麼是redo log (重做日誌又稱為前滾日誌)

我們知道 MySQL 數據存在磁碟中,每次讀寫數據需做磁碟隨機IO,並發場景下性能差。為此 MySQL 引入快取 Buffer Pool 做優化。其包含磁碟中部分數據頁(page)的映射,來緩解資料庫的磁碟壓力。

當從資料庫讀數據時,首先從快取中讀,快取中沒有,則從磁碟讀後放入快取;當向資料庫寫數據時,先向快取中寫,此時快取中的數據
頁數據會變更,該數據頁叫臟頁,Buffer Pool 中修改完數據後會按照設定的策略再定期刷到磁碟中去,這個過程叫刷臟頁

那麼問題來了,如果 Buffer Pool 中修改的數據還沒有及時的刷到磁碟,MySQL 宕機重啟,就會導致數據丟失,無法保證事務的持久性,怎麼辦?

redo log 解決了這個問題。就是說資料庫在修改數據時,會把更新記錄先寫到 redo log 中,再去修改 Buffer Pool 中的數據,當提交事務時,調用 fsync 把 redo log 刷入磁碟。至於快取中更新的數據文件何時刷入磁碟,則由後台執行緒非同步處理。

我們先看一下Mysql數據更新的流程:

image
下面詳細拆解一下圖中容易產生疑問的幾個點:

  1. 首先當用戶對數據進行變更操作時,在數據真正變更之前,存儲引擎層會先將當前數據保存在undolog中形成一個歷史版本,以便回滾的時候使用。
  2. 然後在將臟頁刷新到磁碟之前,存儲引擎層會先將變更數據的記錄寫入到redo log buffer中,並且通過順序IO刷新到磁碟當中的redolog中,將redolog的狀態置為prepare(這裡其實就相當於告訴Binlog我已經記錄好數據變化了,你可以開始更新了)。
  3. 於是接下來server層會進行binlog日誌文件的更新,將數據變化寫入到binlog中,當binlog更新完成後事務才算成功commit,並將commit這個狀態寫入到redolog中。
  4. 最後在執行刷臟頁這個操作,這個刷臟頁的操作是隨機IO

注意:在上述過程中必須要保證redolog和binlog的數據一致性。

  • 如果redo log寫失敗了,而binlog寫成功了。那假設記憶體的數據還沒來得及落磁碟,機器就掛掉了。那主從伺服器的數據就不一致了。(從伺服器通過binlog得到最新的數據,而主伺服器由於redo log沒有記載,沒法恢複數據),所以如果redo log寫失敗了,那我們就認為這次事務有問題,回滾,不再寫binlog。

  • 如果redo log寫成功了,而binlog寫失敗了,主從將無法同步,所以我們還是會對這次的事務進行回滾操作,將無效的binlog給刪除(因為binlog會影響從庫的數據,所以需要做刪除操作)

這也是為什麼redolog要分為兩個階段,是為了保證redolog和binglo的數據一致性。

redo log寫入策略

由innodb_flush_log_at_trx_commit 參數決定。

  • innodb_flush_log_at_trx_commit=1,表示在每次事務提交的時候,都把log buffer刷到文件系統中(os buffer)去,並且調用文件系統的「flush」操作將快取刷新到磁碟上去。這樣的話,資料庫對IO的要求就非常高了,如果底層的硬體提供的IOPS比較差,那麼MySQL資料庫的並發很快就會由於硬體IO的問題而無法提升。
  • innodb_flush_log_at_trx_commit=0 ,表示每隔一秒把log buffer刷到文件系統中(os buffer)去,並且調用文件系統的「flush」操作將快取刷新到磁碟上去。也就是說一秒之前的日誌都保存在日誌緩衝區,也就是記憶體上,如果機器宕掉,可能丟失1秒的事務數據。
  • innodb_flush_log_at_trx_commit=2,表示在每次事務提交的時候會把log buffer刷到文件系統中去,但並不會立即刷寫到磁碟。如果只是MySQL資料庫掛掉了,由於文件系統沒有問題,那麼對應的事務數據並沒有丟失。只有在資料庫所在的主機作業系統損壞或者突然掉電的情況下,資料庫的事務數據可能丟失1秒之類的事務數據。這樣的好處,減少了事務數據丟失的概率,而對底層硬體的IO要求也沒有那麼高(log buffer寫到文件系統中,一般只是從log buffer的記憶體轉移的文件系統的記憶體快取中,對底層IO沒有壓力)。
    image

SOL語句:select @@innodb_flush_log_at_trx_commit;

redo log 的寫入方式?

redo log 採用大小固定,循環寫入的方式,當寫滿後,會重新從頭開始循環寫,類似一個環狀。這樣設計原因是 redo log 記錄的是數據頁上的修改,如果 Buffer Pool 中數據頁已經刷到磁碟,這些記錄就失效了,新日誌會將這些失效的記錄覆蓋擦除。
注意:redo log 滿了,在擦除之前,要確保這些要被擦除記錄都已經刷到磁碟中了。在擦除舊記錄釋放新空間期間,不能再接收新的更新請求,此時 MySQL 性能會下降。因此高並發情況下,合理調整 redo log 大小很重要。

crash-safe 能力是什麼?

Innodb 引擎有 crash-safe 能力,即事務提交過程中任何階段,MySQL 宕機重啟後都能保證事務的完整性,已提交的數據不會丟失。這種能力是通過redo log保證的,MySQL 宕機重啟,系統將自動檢查 redo log,將修改還未寫入磁碟的數據從 redo log 恢復到 MySQL 中。

二、什麼是binlog

binlog 是作為mysql操作記錄歸檔的日誌,這個日誌記錄了所有對資料庫的數據、表結構、索引等等變更的操作。也就是說只要是對資料庫有變更的操作都會記錄到binlog裡面來, 可以把資料庫的數據當成我們銀行賬戶里的餘額,而binlog就相當於我們銀行卡的流水。賬戶餘額只是一個結果,至於這個結果怎麼來的,那就必須得看流水了。而同樣在mysql里我們就是通過binlog來歸檔、驗證、恢復、同步數據。

binlog 記錄內容

binlog應該說是Mysql里最核心的日誌, 它記錄了除了查詢語句(select、show)之外的所有的 DDL 和 DML 語句,也就意味著我們基本上所有對資料庫的操作變更都會記錄到binlog裡面。binlog以事件形式記錄,不僅記錄了操作的語句,同時還記錄了語句所執行的消耗的時間。 binlog 有三種記錄格式,分別是ROW、STATEMENT、MIXED。

  1. statement(5.6默認)SBR(statement based replication) :語句模式原封不動的記錄當前DML。
  2. ROW(5.7 默認值) RBR(ROW based replication) :記錄數據行的變化(用戶看不懂,需要工具分析)
  3. mixed(混合)MBR(mixed based replication)模式 :以上兩種模式的混合

SBR與RBR模式的對比

  • STATEMENT:可讀性較高,日誌量少,但是不夠嚴謹
  • ROW :可讀性很低,日誌量大,足夠嚴謹

三、什麼是undolog

uedo log 是也屬於引擎層(innodb)的日誌,從上面的redo log介紹中我們就已經知道了,redo log 和undo log的核心是為了保證innodb事務機制中的持久性和原子性,事務提交成功由redo log保證數據持久性,而事務可以進行回滾從而保證事務操作原子性則是通過undo log 來保證的。

要對事務數據回滾到歷史的數據狀態,所以我們也能猜到undo log是保存的是數據的歷史版本,通過歷史版本讓數據在任何時候都可以回滾到某一個事務開始之前的狀態。
undo log除了進行事務回滾的日誌外還有一個作用,就是為資料庫實現MVCC多版本並發控制的功能。

啥是回滾和前滾?

(1)回滾
未提交的事務,即事務未執行 commit。但事務內修改的臟頁中,有一部分已刷盤。此時資料庫宕機重啟,需要回滾來將先前那部分已經刷盤的臟塊從磁碟上撤銷。
(2)前滾
未完全提交的事務,即事務已經執行 commit,但該事務內修改的臟頁中只有一部分數據被刷盤,另一部分還在 buffer pool,此時資料庫宕機重啟,就要用前滾來將未來得及刷盤的數據從 redo log 中恢復出來並刷盤。

undo log記錄內容

在Mysql里數據每次修改前,都首先會把修改之前的數據作為歷史保存一份到undo log裡面的,數據裡面會記錄操作該數據的事務ID,然後我們可以通過事務ID來對數據進行回滾。

Tags: