B站一面:MySQL三大日誌(binlog、redo log和undo log)的作用了解嗎?

2021-12-28 程式設計師解析
前言

MySQL 日誌 主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。其中,比較重要的還要屬二進位日誌 binlog(歸檔日誌)和事務日誌 redo log(重做日誌)和 undo log(回滾日誌)。

今天就來聊聊 redo log(重做日誌)、binlog(歸檔日誌)、兩階段提交、undo log (回滾日誌)。

redo log

redo log(重做日誌)是InnoDB存儲引擎獨有的,它讓MySQL擁有了崩潰恢復能力。

比如 MySQL 實例掛了或宕機了,重啟時,InnoDB存儲引擎會使用redo log恢復數據,保證數據的持久性與完整性。

MySQL 中數據是以頁為單位,你查詢一條記錄,會從硬碟把一頁的數據加載出來,加載出來的數據叫數據頁,會放入到 Buffer Pool 中。

後續的查詢都是先從 Buffer Pool 中找,沒有命中再去硬碟加載,減少硬碟 IO 開銷,提升性能。

更新表數據的時候,也是如此,發現 Buffer Pool 裡存在要更新的數據,就直接在 Buffer Pool 裡更新。

然後會把「在某個數據頁上做了什麼修改」記錄到重做日誌緩存(redo log buffer)裡,接著刷盤到 redo log 文件裡。

理想情況,事務一提交就會進行刷盤操作,但實際上,刷盤的時機是根據策略來進行的。

小貼士:每條 redo 記錄由「表空間號+數據頁號+偏移量+修改數據長度+具體修改的數據」組成

刷盤時機

InnoDB 存儲引擎為 redo log 的刷盤策略提供了 innodb_flush_log_at_trx_commit 參數,它支持三種策略:

0 :設置為 0 的時候,表示每次事務提交時不進行刷盤操作1 :設置為 1 的時候,表示每次事務提交時都將進行刷盤操作(默認值)2 :設置為 2 的時候,表示每次事務提交時都只把 redo log buffer 內容寫入 page cache

innodb_flush_log_at_trx_commit 參數默認為 1 ,也就是說當事務提交時會調用 fsync 對 redo log 進行刷盤

另外,InnoDB 存儲引擎有一個後臺線程,每隔1 秒,就會把 redo log buffer 中的內容寫到文件系統緩存(page cache),然後調用 fsync 刷盤。

也就是說,一個沒有提交事務的 redo log 記錄,也可能會刷盤。

為什麼呢?

因為在事務執行過程 redo log 記錄是會寫入redo log buffer 中,這些 redo log 記錄會被後臺線程刷盤。

除了後臺線程每秒1次的輪詢操作,還有一種情況,當 redo log buffer 佔用的空間即將達到 innodb_log_buffer_size 一半的時候,後臺線程會主動刷盤。

下面是不同刷盤策略的流程圖。

innodb_flush_log_at_trx_commit=0

為0時,如果MySQL掛了或宕機可能會有1秒數據的丟失。

innodb_flush_log_at_trx_commit=1

為1時, 只要事務提交成功,redo log記錄就一定在硬碟裡,不會有任何數據丟失。

如果事務執行期間MySQL掛了或宕機,這部分日誌丟了,但是事務並沒有提交,所以日誌丟了也不會有損失。

innodb_flush_log_at_trx_commit=2

為2時, 只要事務提交成功,redo log buffer中的內容只寫入文件系統緩存(page cache)。

如果僅僅只是MySQL掛了不會有任何數據丟失,但是宕機可能會有1秒數據的丟失。

日誌文件組

硬碟上存儲的 redo log 日誌文件不只一個,而是以一個日誌文件組的形式出現的,每個的redo日誌文件大小都是一樣的。

比如可以配置為一組4個文件,每個文件的大小是 1GB,整個 redo log 日誌文件組可以記錄4G的內容。

它採用的是環形數組形式,從頭開始寫,寫到末尾又回到頭循環寫,如下圖所示。

在個日誌文件組中還有兩個重要的屬性,分別是 write pos、checkpoint

write pos 是當前記錄的位置,一邊寫一邊後移checkpoint 是當前要擦除的位置,也是往後推移

每次刷盤 redo log 記錄到日誌文件組中,write pos 位置就會後移更新。

每次 MySQL 加載日誌文件組恢復數據時,會清空加載過的 redo log 記錄,並把 checkpoint 後移更新。

write pos 和 checkpoint 之間的還空著的部分可以用來寫入新的 redo log 記錄。

如果 write pos 追上 checkpoint ,表示日誌文件組滿了,這時候不能再寫入新的 redo log 記錄,MySQL 得停下來,清空一些記錄,把 checkpoint 推進一下。

redo log 小結

相信大家都知道 redo log 的作用和它的刷盤時機、存儲形式。

現在我們來思考一個問題:只要每次把修改後的數據頁直接刷盤不就好了,還有 redo log 什麼事?

它們不都是刷盤麼?差別在哪裡?

1 Byte = 8bit
1 KB = 1024 Byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB

實際上,數據頁大小是16KB,刷盤比較耗時,可能就修改了數據頁裡的幾 Byte 數據,有必要把完整的數據頁刷盤嗎?

而且數據頁刷盤是隨機寫,因為一個數據頁對應的位置可能在硬碟文件的隨機位置,所以性能是很差。

如果是寫 redo log,一行記錄可能就佔幾十 Byte,只包含表空間號、數據頁號、磁碟文件偏移 量、更新值,再加上是順序寫,所以刷盤速度很快。

所以用 redo log 形式記錄修改內容,性能會遠遠超過刷數據頁的方式,這也讓資料庫的並發能力更強。

其實內存的數據頁在一定時機也會刷盤,我們把這稱為頁合併,講 Buffer Pool的時候會對這塊細說

binlog

redo log 它是物理日誌,記錄內容是「在某個數據頁上做了什麼修改」,屬於 InnoDB 存儲引擎。

而 binlog 是邏輯日誌,記錄內容是語句的原始邏輯,類似於「給 ID=2 這一行的 c 欄位加 1」,屬於MySQL Server 層。

不管用什麼存儲引擎,只要發生了表數據更新,都會產生 binlog 日誌。

那 binlog 到底是用來幹嘛的?

可以說MySQL資料庫的數據備份、主備、主主、主從都離不開binlog,需要依靠binlog來同步數據,保證數據一致性。

binlog會記錄所有涉及更新數據的邏輯操作,並且是順序寫。

記錄格式

binlog 日誌有三種格式,可以通過binlog_format參數指定。

指定statement,記錄的內容是SQL語句原文,比如執行一條update T set update_time=now() where id=1,記錄的內容如下。

同步數據時,會執行記錄的SQL語句,但是有個問題,update_time=now()這裡會獲取當前系統時間,直接執行會導致與原庫的數據不一致。

為了解決這種問題,我們需要指定為row,記錄的內容不再是簡單的SQL語句了,還包含操作的具體數據,記錄內容如下。

row格式記錄的內容看不到詳細信息,要通過mysqlbinlog工具解析出來。

update_time=now()變成了具體的時間update_time=1627112756247,條件後面的@1、@2、@3 都是該行數據第 1 個~3 個欄位的原始值(假設這張表只有 3 個欄位)。

這樣就能保證同步數據的一致性,通常情況下都是指定為row,這樣可以為資料庫的恢復與同步帶來更好的可靠性。

但是這種格式,需要更大的容量來記錄,比較佔用空間,恢復與同步時會更消耗IO資源,影響執行速度。

所以就有了一種折中的方案,指定為mixed,記錄的內容是前兩者的混合。

MySQL會判斷這條SQL語句是否可能引起數據不一致,如果是,就用row格式,否則就用statement格式。

寫入機制

binlog的寫入時機也非常簡單,事務執行過程中,先把日誌寫到binlog cache,事務提交的時候,再把binlog cache寫到binlog文件中。

因為一個事務的binlog不能被拆開,無論這個事務多大,也要確保一次性寫入,所以系統會給每個線程分配一個塊內存作為binlog cache。

我們可以通過binlog_cache_size參數控制單個線程 binlog cache 大小,如果存儲內容超過了這個參數,就要暫存到磁碟(Swap)。

binlog日誌刷盤流程如下

上圖的 write,是指把日誌寫入到文件系統的 page cache,並沒有把數據持久化到磁碟,所以速度比較快

write和fsync的時機,可以由參數sync_binlog控制,默認是0。

為0的時候,表示每次提交事務都只write,由系統自行判斷什麼時候執行fsync。

雖然性能得到提升,但是機器宕機,page cache裡面的 binglog 會丟失。

為了安全起見,可以設置為1,表示每次提交事務都會執行fsync,就如同binlog 日誌刷盤流程一樣。

最後還有一種折中方式,可以設置為N(N>1),表示每次提交事務都write,但累積N個事務後才fsync。

在出現IO瓶頸的場景裡,將sync_binlog設置成一個比較大的值,可以提升性能。

同樣的,如果機器宕機,會丟失最近N個事務的binlog日誌。

兩階段提交

redo log(重做日誌)讓InnoDB存儲引擎擁有了崩潰恢復能力。

binlog(歸檔日誌)保證了MySQL集群架構的數據一致性。

雖然它們都屬於持久化的保證,但是則重點不同。

在執行更新語句過程,會記錄redo log與binlog兩塊日誌,以基本的事務為單位,redo log在事務執行過程中可以不斷寫入,而binlog只有在提交事務時才寫入,所以redo log與binlog的寫入時機不一樣。

回到正題,redo log與binlog兩份日誌之間的邏輯不一致,會出現什麼問題?

我們以update語句為例,假設id=2的記錄,欄位c值是0,把欄位c值更新成1,SQL語句為update T set c=1 where id=2。

假設執行過程中寫完redo log日誌後,binlog日誌寫期間發生了異常,會出現什麼情況呢?

由於binlog沒寫完就異常,這時候binlog裡面沒有對應的修改記錄。因此,之後用binlog日誌恢復數據時,就會少這一次更新,恢復出來的這一行c值是0,而原庫因為redo log日誌恢復,這一行c值是1,最終數據不一致。

為了解決兩份日誌之間的邏輯一致問題,InnoDB存儲引擎使用兩階段提交方案。

原理很簡單,將redo log的寫入拆成了兩個步驟prepare和commit,這就是兩階段提交

使用兩階段提交後,寫入binlog時發生異常也不會有影響,因為MySQL根據redo log日誌恢復數據時,發現redo log還處於prepare階段,並且沒有對應binlog日誌,就會回滾該事務。

再看一個場景,redo log設置commit階段發生異常,那會不會回滾事務呢?

並不會回滾事務,它會執行上圖框住的邏輯,雖然redo log是處於prepare階段,但是能通過事務id找到對應的binlog日誌,所以MySQL認為是完整的,就會提交事務恢復數據。

undo log

這部分內容為 JavaGuide 的補充:

我們知道如果想要保證事務的原子性,就需要在異常發生時,對已經執行的操作進行回滾,在 MySQL 中,恢復機制是通過 回滾日誌(undo log) 實現的,所有事務進行的修改都會先先記錄到這個回滾日誌中,然後再執行相關的操作。如果執行過程中遇到異常的話,我們直接利用 回滾日誌 中的信息將數據回滾到修改之前的樣子即可!並且,回滾日誌會先於數據持久化到磁碟上。這樣就保證了即使遇到資料庫突然宕機等情況,當用戶再次啟動資料庫的時候,資料庫還能夠通過查詢回滾日誌來回滾將之前未完成的事務。

另外,MVCC 的實現依賴於:隱藏欄位、Read View、undo log。在內部實現中,InnoDB 通過數據行的 DB_TRX_ID 和 Read View 來判斷數據的可見性,如不可見,則通過數據行的 DB_ROLL_PTR 找到 undo log 中的歷史版本。每個事務讀到的數據版本可能是不一樣的,在同一個事務中,用戶只能看到該事務創建 Read View 之前已經提交的修改和該事務本身做的修改

總結

這部分內容為 JavaGuide 的補充:

MySQL InnoDB 引擎使用 redo log(重做日誌) 保證事務的持久性,使用 undo log(回滾日誌) 來保證事務的原子性

MySQL資料庫的數據備份、主備、主主、主從都離不開binlog,需要依靠binlog來同步數據,保證數據一致性。

相關焦點

  • 必須了解的mysql三大日誌-binlog、redo log和undo log
    日誌是 mysql 資料庫的重要組成部分,記錄著資料庫運行期間各種狀態信息。mysql日誌主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。作為開發,我們重點需要關注的是二進位日誌( binlog )和事務日誌(包括redo log 和 undo log ),本文接下來會詳細介紹這三種日誌。
  • 必須了解的MySQL三大日誌:binlog、redo log和undo log
    日誌是mysql資料庫的重要組成部分,記錄著資料庫運行期間各種狀態信息。mysql日誌主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。
  • 面試題:MySQL三大日誌(binlog、redo log和undo log)的作用了解嗎?
    前言MySQL 日誌 主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。其中,比較重要的還要屬二進位日誌 binlog(歸檔日誌)和事務日誌 redo log(重做日誌)和 undo log(回滾日誌)。
  • 面試官:說說你對binlog、redo log和undo log的理解
    mysql日誌主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。接下來,我們就來對這三個log進行簡單的聊聊,至少在面試中,咱們心裡有底。binlog
  • 面試官:說說binlog、redo log和undo log都是幹啥的
    mysql日誌主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。接下來,我們就來對這三個log進行簡單的聊聊,至少在面試中,咱們心裡有底。binlogbinlog使用場景
  • MySQL 日誌(redo log 和 undo log) 都是什麼鬼?
    出處:https://www.cnblogs.com/f-ck-need-u/innodb事務日誌包括redo log和undo log。redo log是重做日誌,提供前滾操作,undo log是回滾日誌,提供回滾操作。
  • binlog/redo log/undo log?再也不會傻傻分不清楚了
    mysql日誌主要包括錯誤日誌、查詢日誌、慢查詢日誌、事務日誌、二進位日誌幾大類。作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。# binlogbinlog用於記錄資料庫執行的寫入性操作(不包括查詢)信息,以二進位的形式保存在磁碟中。
  • MySQL系列:redo log、binlog 套娃九連擊
    1日誌問題無限套娃在 MySQL系列:更新語句是如何執行的 中,有 binlog(歸檔日誌) 和redo log(重做日誌) 配合奔潰恢復的時候,用反證法說明如果沒有兩階段提交,會導致 mysql 主備數據不一致等問題。
  • 【140期】MySQL中的redolog,undolog,以及binlog的區別及各自作用是什麼?
    ,分別是:重做日誌(redo log)、回滾日誌(undo log)、二進位日誌(binlog)、錯誤日誌(errorlog)、慢查詢日誌(slow query log)、一般查詢日誌(general log),中繼日誌(relay log)。
  • MySQL的undo log
    作用從概念的定義不難看出undo log的兩個作用:事務回滾 - 原子性: undo log是為了實現事務的原子性而出現的產物,事務處理的過程中,如果出現了錯誤或者用戶執行ROLLBACK語句,MySQL可以利用undo log中的備份將數據恢復到事務開始之前的狀態。
  • 詳解 MySQL 的 undo log
    文章導讀undo log文章導讀概念undo log是innodb引擎的一種日誌,在事務的修改記錄之前,會把該記錄的原值(before image)先保存起來(undo log)再做修改,以便修改過程中出錯能夠恢復原值或者其他的事務讀取。
  • 詳解MySQL的Redo日誌與Undo日誌
    因為在redo日誌有commit或abort記錄的事務是無需undo的。不同的資料庫部件有各自的設計目的,負責不同的命令,Read和Write由事務發起,Input和Output由緩衝區管理器發出。也就是說,日誌記錄響應的是寫入內存的write命令,而不是寫入磁碟的output命令,除非顯示的控制。具體的實現上會有很多策略,但應保證一些原則:針對undo1.如果事務T改變了資料庫元素X,那麼必須保證對應的一條undo記錄在X的新值寫入磁碟之前落盤。
  • Mysql的binlog和relay-log到底長啥樣?
    上一篇mysql面試的文章之後收到不少朋友的意見,希望深入講講複製、日誌的格式這些,今天,我們就來深挖一下mysql的複製機制到底有哪一些,以及binlog和relay-log的結構到底是什麼樣子的。binlog作用binlog的主要作用是記錄資料庫中表的更改,它只記錄改變數據的sql,不改變數據的sql不會寫入,比如select語句一般不會被記錄,因為他們不會對數據產生任何改動。
  • MySQL 中的 binlog 和 relay-log 結構完全詳解
    今天我們來深挖一下mysql的複製機制到底有哪一些,以及binlog和relay-log的結構到底是什麼樣子的。binlog作用binlog的主要作用是記錄資料庫中表的更改,它只記錄改變數據的sql,不改變數據的sql不會寫入,比如select語句一般不會被記錄,因為他們不會對數據產生任何改動。
  • 資料庫面試題:redo log和binlog的區別、一條更新語句的執行過程
    資料庫面試題:redo log和binlog的區別1.redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用;2.redo log是物理日誌,記錄的是
  • MySQL如何計算統計redo log大小
    在MySQL中如何計算、統計重做日誌(redo log)的生成情況呢? 例如10分鐘內,生成了多少M的redo log呢?30分鐘內又生成了多少M的redo log.....。MySQL沒有像Oracle中那樣的系統視圖統計這些數據,但是我們可以通過一些方法曲線的統計二進位日誌的生成量。
  • 美團二面:如何解決 bin log 與 redo log 的一致性問題
    「追加寫」 是指 bin log 文件寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌可以看到,redo log 和 bin log 的一個很大的區別就是,一個是循環寫,一個是追加寫。也就是說 redo log 只會記錄未刷入磁碟的日誌,已經刷入磁碟的數據都會從 redo log 這個有限大小的日誌文件裡刪除。
  • InnoDB undo log
    分配事務ID和回滾段讀寫事務只讀事務轉成讀寫事務分配時機分配模式普通回滾段分配方式使用回滾段1.判斷當前變更是否是臨時表,如果是臨時表,則採用臨時表回滾段來分配,否則採用普通回滾段2.臨時表操作記錄undo時不寫redo3.操作類型為TRX_UNDO_INSERT_OP,且未分配
  • binlog和relay-log到底長啥樣?
    本文公眾號來源:科技繆繆今天,我們就來深挖一下mysql的複製機制到底有哪一些,以及binlog和relay-log的結構到底是什麼樣子的。binlog作用binlog的主要作用是記錄資料庫中表的更改,它只記錄改變數據的sql,不改變數據的sql不會寫入,比如select語句一般不會被記錄,因為他們不會對數據產生任何改動。
  • mysql分布式事務,undo和redo,java學習這塊必須要懂?
    因為在傳統項目中,項目部署基本是單點式:即單個伺服器和單個資料庫。這種情況下,資料庫本身的事務機制就能保證ACID的原則,這樣的事務就是本地事務。概括來講,單個服務與單個資料庫的架構中,產生的事務都是本地事務。其中原子性和持久性就要靠undo和redo 日誌來實現。