MySQL三大日志(binlog、redo?log和undo?log)圖文詳解
1.redo log
redo log概述
redo log(重做日志)是InnoDB存儲引擎獨有的,它讓MySQL擁有了崩潰恢復能力。
比如 MySQL 實例掛了或宕機了,重啟時,InnoDB存儲引擎會使用redo log恢復數(shù)據(jù),保證數(shù)據(jù)的持久性與完整性。

MySQL 中數(shù)據(jù)是以頁為單位,你查詢一條記錄,會從硬盤把一頁的數(shù)據(jù)加載出來,加載出來的數(shù)據(jù)叫數(shù)據(jù)頁,會放入到 Buffer Pool 中。
后續(xù)的查詢都是先從 Buffer Pool 中找,沒有命中再去硬盤加載,減少硬盤 IO 開銷,提升性能。
更新表數(shù)據(jù)的時候,也是如此,發(fā)現(xiàn) Buffer Pool 里存在要更新的數(shù)據(jù),就直接在 Buffer Pool 里更新。
然后會把“在某個數(shù)據(jù)頁上做了什么修改”記錄到重做日志緩存(redo log buffer)里,接著刷盤到 redo log 文件里。

刷盤時機
InnoDB 存儲引擎為 redo log 的刷盤策略提供了 innodb_flush_log_at_trx_commit 參數(shù),它支持三種策略:
- 0 :設置為 0 的時候,表示每次事務提交時不進行刷盤操作
- 1 :設置為 1 的時候,表示每次事務提交時都將進行刷盤操作 (默認值)
- 2 :設置為 2 的時候,表示每次事務提交時都只把 redo log buffer 內容寫入 page cache
另外,InnoDB 存儲引擎有一個后臺線程,每隔1 秒,就會把 redo log buffer 中的內容寫到文件系統(tǒng)緩存(page cache),然后調用 fsync 刷盤。

也就是說,一個沒有提交事務的
redo log記錄,也可能會刷盤??
除了后臺線程每秒1次的輪詢操作,還有一種情況,當 redo log buffer 占用的空間即將達到 innodb_log_buffer_size 一半的時候,后臺線程會主動刷盤。
innodb_flush_log_at_trx_commit=0

為
0時,如果MySQL掛了或宕機可能會有1秒數(shù)據(jù)的丟失。??
innodb_flush_log_at_trx_commit=1

為1時, 只要事務提交成功,redo log記錄就一定在硬盤里,不會有任何數(shù)據(jù)丟失。
如果事務執(zhí)行期間MySQL掛了或宕機,這部分日志丟了,但是事務并沒有提交,所以日志丟了也不會有損失。
innodb_flush_log_at_trx_commit=2

為2時, 只要事務提交成功,redo log buffer中的內容只寫入文件系統(tǒng)緩存(page cache)。
如果僅僅只是MySQL掛了不會有任何數(shù)據(jù)丟失,但是宕機可能會有1秒數(shù)據(jù)的丟失。
日志文件組
硬盤上存儲的 redo log 日志文件不只一個,而是以一個日志文件組的形式出現(xiàn)的,每個的redo日志文件大小都是一樣的。
它采用的是環(huán)形數(shù)組形式,從頭開始寫,寫到末尾又回到頭循環(huán)寫,如下圖所示。

2.binlog binlog
概述
redo log 它是物理日志,記錄內容是“在某個數(shù)據(jù)頁上做了什么修改”,屬于 InnoDB 存儲引擎。
而 binlog 是邏輯日志,記錄內容是語句的原始邏輯,類似于“給 ID=2 這一行的 c 字段加 1”,屬于MySQL Server 層。
不管用什么存儲引擎,只要發(fā)生了表數(shù)據(jù)更新,都會產(chǎn)生
binlog日志。??
可以說MySQL數(shù)據(jù)庫的數(shù)據(jù)備份、主備、主主、主從都離不開binlog,需要依靠binlog來同步數(shù)據(jù),保證數(shù)據(jù)一致性。

binlog會記錄所有涉及更新數(shù)據(jù)的邏輯操作,并且是順序寫。
記錄格式
binlog 日志有三種格式,可以通過binlog_format參數(shù)指定。
- statement
- row
- mixed
指定statement,記錄的內容是SQL語句原文,比如執(zhí)行一條update T set update_time=now() where id=1,記錄的內容如下。

同步數(shù)據(jù)時,會執(zhí)行記錄的SQL語句,但是有個問題,update_time=now()這里會獲取當前系統(tǒng)時間,直接執(zhí)行會導致與原庫的數(shù)據(jù)不一致。
為了解決這種問題,我們需要指定為row,記錄的內容不再是簡單的SQL語句了,還包含操作的具體數(shù)據(jù),記錄內容如下。

這樣就能保證同步數(shù)據(jù)的一致性,通常情況下都是指定為row,這樣可以為數(shù)據(jù)庫的恢復與同步帶來更好的可靠性。
但是這種格式,需要更大的容量來記錄,比較占用空間,恢復與同步時會更消耗IO資源,影響執(zhí)行速度。
所以就有了一種折中的方案,指定為mixed,記錄的內容是前兩者的混合。
MySQL會判斷這條SQL語句是否可能引起數(shù)據(jù)不一致,如果是,就用row格式,否則就用statement格式。
寫入機制
binlog的寫入時機也非常簡單,事務執(zhí)行過程中,先把日志寫到binlog cache,事務提交的時候,再把binlog cache寫到binlog文件中。
因為一個事務的binlog不能被拆開,無論這個事務多大,也要確保一次性寫入,所以系統(tǒng)會給每個線程分配一個塊內存作為binlog cache。
我們可以通過binlog_cache_size參數(shù)控制單個線程 binlog cache 大小,如果存儲內容超過了這個參數(shù),就要暫存到磁盤(Swap)。
binlog日志刷盤流程如下:

- 上圖的 write,是指把日志寫入到文件系統(tǒng)的 page cache,并沒有把數(shù)據(jù)持久化到磁盤,所以速度比較快
- 上圖的 fsync,才是將數(shù)據(jù)持久化到磁盤的操作
刷盤時機
write和fsync的時機,可以由參數(shù)sync_binlog控制,默認是0。
為0的時候,表示每次提交事務都只write,由系統(tǒng)自行判斷什么時候執(zhí)行fsync。

雖然性能得到提升,但是機器宕機,page cache里面的 binlog 會丟失。
為了安全起見,可以設置為1,表示每次提交事務都會執(zhí)行fsync,就如同 redo log 日志刷盤流程 一樣。
最后還有一種折中方式,可以設置為N(N>1),表示每次提交事務都write,但累積N個事務后才fsync。

在出現(xiàn)
IO瓶頸的場景里,將sync_binlog設置成一個比較大的值,可以提升性能。同樣的,如果機器宕機,會丟失最近N個事務的binlog日志。??
3.兩階段提交
redo log(重做日志)讓InnoDB存儲引擎擁有了崩潰恢復能力。
binlog(歸檔日志)保證了MySQL集群架構的數(shù)據(jù)一致性。
雖然它們都屬于持久化的保證,但是側重點不同。
在執(zhí)行更新語句過程,會記錄redo log與binlog兩塊日志,以基本的事務為單位,redo log在事務執(zhí)行過程中可以不斷寫入,而binlog只有在提交事務時才寫入,所以redo log與binlog的寫入時機不一樣。

回到正題,redo log與binlog兩份日志之間的邏輯不一致,會出現(xiàn)什么問題?
我們以update語句為例,假設id=2的記錄,字段c值是0,把字段c值更新成1,SQL語句為update T set c=1 where id=2。
假設執(zhí)行過程中寫完redo log日志后,binlog日志寫期間發(fā)生了異常,會出現(xiàn)什么情況呢?

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

為了解決兩份日志之間的邏輯一致問題,InnoDB存儲引擎使用兩階段提交方案。
原理很簡單,將redo log的寫入拆成了兩個步驟prepare和commit,這就是兩階段提交。

使用兩階段提交后,寫入binlog時發(fā)生異常也不會有影響,因為MySQL根據(jù)redo log日志恢復數(shù)據(jù)時,發(fā)現(xiàn)redo log還處于prepare階段,并且沒有對應binlog日志,就會回滾該事務。

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

并不會回滾事務,它會執(zhí)行上圖框住的邏輯,雖然redo log是處于prepare階段,但是能通過事務id找到對應的binlog日志,所以MySQL認為是完整的,就會提交事務恢復數(shù)據(jù)。
4.undo log
我們知道如果想要保證事務的原子性,就需要在異常發(fā)生時,對已經(jīng)執(zhí)行的操作進行回滾,在 MySQL 中,恢復機制是通過 回滾日志(undo log) 實現(xiàn)的,所有事務進行的修改都會先記錄到這個回滾日志中,然后再執(zhí)行相關的操作。如果執(zhí)行過程中遇到異常的話,我們直接利用 回滾日志 中的信息將數(shù)據(jù)回滾到修改之前的樣子即可!并且,回滾日志會先于數(shù)據(jù)持久化到磁盤上。這樣就保證了即使遇到數(shù)據(jù)庫突然宕機等情況,當用戶再次啟動數(shù)據(jù)庫的時候,數(shù)據(jù)庫還能夠通過查詢回滾日志來回滾將之前未完成的事務。
另外,MVCC 的實現(xiàn)依賴于:隱藏字段、Read View、undo log。在內部實現(xiàn)中,InnoDB 通過數(shù)據(jù)行的 DB_TRX_ID 和 Read View 來判斷數(shù)據(jù)的可見性,如不可見,則通過數(shù)據(jù)行的 DB_ROLL_PTR 找到 undo log 中的歷史版本。每個事務讀到的數(shù)據(jù)版本可能是不一樣的,在同一個事務中,用戶只能看到該事務創(chuàng)建 Read View 之前已經(jīng)提交的修改和該事務本身做的修改
5.總結
MySQL InnoDB 引擎使用 redo log(重做日志) 保證事務的持久性,使用 undo log(回滾日志) 來保證事務的原子性。
MySQL數(shù)據(jù)庫的數(shù)據(jù)備份、主備、主主、主從都離不開binlog,需要依靠binlog來同步數(shù)據(jù),保證數(shù)據(jù)一致性。
本教程基于JavaGuide - 數(shù)據(jù)庫篇 - 三大日志詳解
總結
到此這篇關于MySQL三大日志(binlog、redo log和undo log)的文章就介紹到這了,更多相關MySQL日志binlog、redo log和undo log內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Oracle和MySQL中生成32位uuid的方法舉例(國產(chǎn)達夢同Oracle)
近日遇到朋友問及如何生成UUID,UUID是通用唯一識別碼(Universally Unique Identifier)方法,這里給大家總結下,這篇文章主要給大家介紹了關于Oracle和MySQL中生成32位uuid的方法,需要的朋友可以參考下2023-08-08
MySQL無法啟動、無法停止解決方法(安全設置后容易出現(xiàn))
最近在Win2003上的MySQL出現(xiàn)過多次正常運行時無法連接數(shù)據(jù)庫故障,根本原因就是因為安全設置以后容易出現(xiàn)的問題,其實很簡單的解決2012-03-03
虛擬機linux端mysql數(shù)據(jù)庫無法遠程訪問的解決辦法
最近在項目搭建過程中遇到一問題,有關虛擬機linux端mysql數(shù)據(jù)庫無法遠程訪問,通過查閱相關數(shù)據(jù)庫資料問題解決,下面把具體的解決辦法分享給大家,有需要的朋友可以參考下2015-08-08
MySQL之information_schema數(shù)據(jù)庫詳細講解
這篇文章主要介紹了MySQL之information_schema數(shù)據(jù)庫詳細講解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-08-08

