MySQL三大日志(binlog、redo?log和undo?log)圖文詳解
1.redo log
redo log概述
redo log
(重做日志)是InnoDB
存儲引擎獨有的,它讓MySQL
擁有了崩潰恢復(fù)能力。
比如 MySQL
實例掛了或宕機了,重啟時,InnoDB
存儲引擎會使用redo log
恢復(fù)數(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 :設(shè)置為 0 的時候,表示每次事務(wù)提交時不進行刷盤操作
- 1 :設(shè)置為 1 的時候,表示每次事務(wù)提交時都將進行刷盤操作 (默認值)
- 2 :設(shè)置為 2 的時候,表示每次事務(wù)提交時都只把 redo log buffer 內(nèi)容寫入 page cache
另外,InnoDB
存儲引擎有一個后臺線程,每隔1
秒,就會把 redo log buffer
中的內(nèi)容寫到文件系統(tǒng)緩存(page cache
),然后調(diào)用 fsync
刷盤。
也就是說,一個沒有提交事務(wù)的
redo log
記錄,也可能會刷盤??
除了后臺線程每秒1
次的輪詢操作,還有一種情況,當(dāng) 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
時, 只要事務(wù)提交成功,redo log
記錄就一定在硬盤里,不會有任何數(shù)據(jù)丟失。
如果事務(wù)執(zhí)行期間MySQL
掛了或宕機,這部分日志丟了,但是事務(wù)并沒有提交,所以日志丟了也不會有損失。
innodb_flush_log_at_trx_commit=2
為2
時, 只要事務(wù)提交成功,redo log buffer
中的內(nèi)容只寫入文件系統(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
它是物理日志,記錄內(nèi)容是“在某個數(shù)據(jù)頁上做了什么修改”,屬于 InnoDB
存儲引擎。
而 binlog
是邏輯日志,記錄內(nèi)容是語句的原始邏輯,類似于“給 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ù)的邏輯操作,并且是順序?qū)憽?/p>
記錄格式
binlog
日志有三種格式,可以通過binlog_format
參數(shù)指定。
- statement
- row
- mixed
指定statement
,記錄的內(nèi)容是SQL
語句原文,比如執(zhí)行一條update T set update_time=now() where id=1
,記錄的內(nèi)容如下。
同步數(shù)據(jù)時,會執(zhí)行記錄的SQL
語句,但是有個問題,update_time=now()
這里會獲取當(dāng)前系統(tǒng)時間,直接執(zhí)行會導(dǎo)致與原庫的數(shù)據(jù)不一致。
為了解決這種問題,我們需要指定為row
,記錄的內(nèi)容不再是簡單的SQL
語句了,還包含操作的具體數(shù)據(jù),記錄內(nèi)容如下。
這樣就能保證同步數(shù)據(jù)的一致性,通常情況下都是指定為row
,這樣可以為數(shù)據(jù)庫的恢復(fù)與同步帶來更好的可靠性。
但是這種格式,需要更大的容量來記錄,比較占用空間,恢復(fù)與同步時會更消耗IO
資源,影響執(zhí)行速度。
所以就有了一種折中的方案,指定為mixed
,記錄的內(nèi)容是前兩者的混合。
MySQL
會判斷這條SQL
語句是否可能引起數(shù)據(jù)不一致,如果是,就用row
格式,否則就用statement
格式。
寫入機制
binlog
的寫入時機也非常簡單,事務(wù)執(zhí)行過程中,先把日志寫到binlog cache
,事務(wù)提交的時候,再把binlog cache
寫到binlog
文件中。
因為一個事務(wù)的binlog
不能被拆開,無論這個事務(wù)多大,也要確保一次性寫入,所以系統(tǒng)會給每個線程分配一個塊內(nèi)存作為binlog cache
。
我們可以通過binlog_cache_size
參數(shù)控制單個線程 binlog cache 大小,如果存儲內(nèi)容超過了這個參數(shù),就要暫存到磁盤(Swap
)。
binlog
日志刷盤流程如下:
- 上圖的 write,是指把日志寫入到文件系統(tǒng)的 page cache,并沒有把數(shù)據(jù)持久化到磁盤,所以速度比較快
- 上圖的 fsync,才是將數(shù)據(jù)持久化到磁盤的操作
刷盤時機
write
和fsync
的時機,可以由參數(shù)sync_binlog
控制,默認是0
。
為0
的時候,表示每次提交事務(wù)都只write
,由系統(tǒng)自行判斷什么時候執(zhí)行fsync
。
雖然性能得到提升,但是機器宕機,page cache
里面的 binlog 會丟失。
為了安全起見,可以設(shè)置為1
,表示每次提交事務(wù)都會執(zhí)行fsync
,就如同 redo log 日志刷盤流程 一樣。
最后還有一種折中方式,可以設(shè)置為N(N>1)
,表示每次提交事務(wù)都write
,但累積N
個事務(wù)后才fsync
。
在出現(xiàn)
IO
瓶頸的場景里,將sync_binlog
設(shè)置成一個比較大的值,可以提升性能。同樣的,如果機器宕機,會丟失最近N
個事務(wù)的binlog
日志。??
3.兩階段提交
redo log
(重做日志)讓InnoDB
存儲引擎擁有了崩潰恢復(fù)能力。
binlog
(歸檔日志)保證了MySQL
集群架構(gòu)的數(shù)據(jù)一致性。
雖然它們都屬于持久化的保證,但是側(cè)重點不同。
在執(zhí)行更新語句過程,會記錄redo log
與binlog
兩塊日志,以基本的事務(wù)為單位,redo log
在事務(wù)執(zhí)行過程中可以不斷寫入,而binlog
只有在提交事務(wù)時才寫入,所以redo log
與binlog
的寫入時機不一樣。
回到正題,redo log
與binlog
兩份日志之間的邏輯不一致,會出現(xiàn)什么問題?
我們以update
語句為例,假設(shè)id=2
的記錄,字段c
值是0
,把字段c
值更新成1
,SQL
語句為update T set c=1 where id=2
。
假設(shè)執(zhí)行過程中寫完redo log
日志后,binlog
日志寫期間發(fā)生了異常,會出現(xiàn)什么情況呢?
由于binlog
沒寫完就異常,這時候binlog
里面沒有對應(yīng)的修改記錄。因此,之后用binlog
日志恢復(fù)數(shù)據(jù)時,就會少這一次更新,恢復(fù)出來的這一行c
值是0
,而原庫因為redo log
日志恢復(fù),這一行c
值是1
,最終數(shù)據(jù)不一致。
為了解決兩份日志之間的邏輯一致問題,InnoDB
存儲引擎使用兩階段提交方案。
原理很簡單,將redo log
的寫入拆成了兩個步驟prepare
和commit
,這就是兩階段提交。
使用兩階段提交后,寫入binlog
時發(fā)生異常也不會有影響,因為MySQL
根據(jù)redo log
日志恢復(fù)數(shù)據(jù)時,發(fā)現(xiàn)redo log
還處于prepare
階段,并且沒有對應(yīng)binlog
日志,就會回滾該事務(wù)。
再看一個場景,redo log
設(shè)置commit
階段發(fā)生異常,那會不會回滾事務(wù)呢?
并不會回滾事務(wù),它會執(zhí)行上圖框住的邏輯,雖然redo log
是處于prepare
階段,但是能通過事務(wù)id
找到對應(yīng)的binlog
日志,所以MySQL
認為是完整的,就會提交事務(wù)恢復(fù)數(shù)據(jù)。
4.undo log
我們知道如果想要保證事務(wù)的原子性,就需要在異常發(fā)生時,對已經(jīng)執(zhí)行的操作進行回滾,在 MySQL 中,恢復(fù)機制是通過 回滾日志(undo log) 實現(xiàn)的,所有事務(wù)進行的修改都會先記錄到這個回滾日志中,然后再執(zhí)行相關(guān)的操作。如果執(zhí)行過程中遇到異常的話,我們直接利用 回滾日志 中的信息將數(shù)據(jù)回滾到修改之前的樣子即可!并且,回滾日志會先于數(shù)據(jù)持久化到磁盤上。這樣就保證了即使遇到數(shù)據(jù)庫突然宕機等情況,當(dāng)用戶再次啟動數(shù)據(jù)庫的時候,數(shù)據(jù)庫還能夠通過查詢回滾日志來回滾將之前未完成的事務(wù)。
另外,MVCC
的實現(xiàn)依賴于:隱藏字段、Read View、undo log。在內(nèi)部實現(xiàn)中,InnoDB
通過數(shù)據(jù)行的 DB_TRX_ID
和 Read View
來判斷數(shù)據(jù)的可見性,如不可見,則通過數(shù)據(jù)行的 DB_ROLL_PTR
找到 undo log
中的歷史版本。每個事務(wù)讀到的數(shù)據(jù)版本可能是不一樣的,在同一個事務(wù)中,用戶只能看到該事務(wù)創(chuàng)建 Read View
之前已經(jīng)提交的修改和該事務(wù)本身做的修改
5.總結(jié)
MySQL InnoDB 引擎使用 redo log(重做日志) 保證事務(wù)的持久性,使用 undo log(回滾日志) 來保證事務(wù)的原子性。
MySQL
數(shù)據(jù)庫的數(shù)據(jù)備份、主備、主主、主從都離不開binlog
,需要依靠binlog
來同步數(shù)據(jù),保證數(shù)據(jù)一致性。
本教程基于JavaGuide - 數(shù)據(jù)庫篇 - 三大日志詳解
總結(jié)
到此這篇關(guān)于MySQL三大日志(binlog、redo log和undo log)的文章就介紹到這了,更多相關(guān)MySQL日志binlog、redo log和undo log內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MYSQL初始化數(shù)據(jù)目錄的實現(xiàn)步驟
本文主要介紹了MYSQL初始化數(shù)據(jù)目錄的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02mysql-8.0.30壓縮包版安裝和配置MySQL環(huán)境過程
該文章介紹了如何在Windows系統(tǒng)中下載、安裝和配置MySQL數(shù)據(jù)庫,包括下載地址、解壓文件、創(chuàng)建和配置my.ini文件、設(shè)置環(huán)境變量、初始化MySQL服務(wù)、啟動服務(wù)以及修改root用戶密碼等步驟2025-01-01mysql關(guān)聯(lián)兩張表時的編碼問題及解決辦法
在本篇文章里小編給大家整理的是關(guān)于mysql關(guān)聯(lián)兩張表時的編碼問題及解決辦法,有需要的朋友們可以參考下。2019-09-09阿里云ECS centos6.8下安裝配置MySql5.7的教程
阿里云默認yum命令下的MySQL是5.17****,安裝mysql5.7之前先卸載以前的版本。下面通過本文給大家介紹阿里云ECS centos6.8下安裝配置MySql5.7的教程,需要的的朋友參考下吧2017-07-07