https://www.jianshu.com/p/907f9002442e
一文帶你看懂binlog和redo log
在介紹binlog和redolog之前,有必要先簡單介紹一下MySQL的邏輯架構。總體上來說,MySQL可以分爲server層和engine層兩部分,如下圖所示:
image
其中server層包括連接池、查詢緩存、分析器、優化器等部分,MySQL的大多數核心服務都在這一層,而engine層就是其插件式的存儲引擎,主要負責數據的存儲和讀取。
1.binlog
1.1binlog作用
我們今天要講的binlog就是server層產生的日誌,因此你知道,不管你使用的是哪一種存儲引擎,都會產生binlog。那問題來了,binlog有什麼用呢?
要知道它能「做什麼」,需要先知道它「是什麼」。簡單來說,binlog其實就是記錄了MySQL對數據庫執行更改的所有操作,因此很顯然,它可以用來做數據歸檔和數據恢復。
1.2binlog格式
在MySQL5.1之前,所有的binlog都是基於SQL語句級別的。但是應用這種格式的binlog進行數據恢復時,如果SQL語句帶有rand或uuid等函數,可能導致恢復出來的數據與原始數據不一致。因此從5.1版本開始,MySQL引入了binlog_format參數,該參數有三種可選值:statement、row和mixed:
- statement就是之前的格式,基於SQL語句來記錄
- row記錄的則是行的更改情況,可以避免之前提到的數據不一致的問題
- 但是row格式有一個不好的地方就是當修改的行數很多時,生成的binlog佔用很大的空間,佔用大量空間的同時還會耗費大量IO資源,因此MySQL又提供了一種折中的方案——mixed。在mixed模式下,MySQL默認仍然採用statement格式進行記錄,但是一旦它判斷可能會有數據不一致的情況發生,則會採用row格式來記錄
我們可以通過這條語句來查看MySQL當前的binlog格式:
image
1.3binlog內容 —— statement格式
爲了查看binlog的具體內容,我們先創建一張表並對它進行初始化:
CREATE TABLE `t` (
`id` int(10) NOT NULL auto_increment,
`a` int(10) DEFAULT NULL,
`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t values(null, 1,'2019-05-01');
insert into t values(null, 2,'2019-05-02');
insert into t values(null, 3,'2019-05-03');
查看當前binlog文件列表:
show master logs;
image
查看當前正在寫入的binlog文件:
show master status;
image
查看binlog文件內容:
show binlog events in 'mysql-bin.000006';
image
可以看到,我們執行的insert語句被包裹在一個事務當中,同時binlog原原本本記錄了我們的SQL語句。除此之外,我們還發現了另外一些東西:
- 第一行的
SET@@SESSION.GTID_NEXT='ANONYMOUS'
主要用於在主備複製時,將主庫的gtid集合同步到備庫 - 第三行的INSERT_ID=3表示插入該行時的自增id=3
- 第四行在我們的原始SQL語句之前的多了一句use test;這可以保證我們的SQL語句只會在test庫執行
- 第五行
COMMIT/* xid=1557 */
中的xid有兩個作用:其一:通過有無xid可以判斷一個事務的binlog是否完整;其二:redo log中也有xid,通過這個字段可以將binlog和redo log關聯起來
1.4binlog內容 —— row格式
執行以下語句將binlog格式切換爲row,看看row格式下的binlog有何不同:
set session binlog_format = 'row';
插入一條新數據:
insert into t values (null, 4, '2019-05-04');
查看binlog如下:
image
可以發現,變化在於第3行和第4行,Intvar和Query兩個事件變成了Tablemap和Writerows。但是從這些信息中我們只能知道該事務對錶test.t做了插入操作,確並不知道插入的具體數據是什麼。
還好我們有mysqlbinlog工具,藉助它我們可以一窺究竟。
這裏有一點需要說明的是,mysqlbinlog不是MySQL數據庫的語法,而只是客戶端提供的一個工具,因此不需要登錄數據庫!!!不需要!!!說來慚愧,這個問題困擾了我好久,怎麼就一直提示我語法錯誤呢?o(╯□╰)o
image
另外執行mysqlbinlog命令必須指定binlog文件的具體路徑,筆者使用的是5.7版本,在該版本中,binlog存儲路徑的默認配置爲log-bin=mysql-bin。可是mysql-bin具體哪個路徑呢?這個也困擾了我一段時間,其實很簡單,find一下就知道了。
image
好,現在切到該目錄下執行命令,重點關注紅框中的內容。不難發現,與statement模式記錄完整SQL語句不通,在row模式下,binlog記錄的表中每個字段的值。
image
1.5binlog內容 —— mixed格式
前面提到,在mixed模式下,MySQL默認仍然採用statement格式進行記錄,但是一旦它判斷可能會有數據不一致的情況發生,則會採用row格式來記錄,大家可以自行進行試驗查看。
2.redo log
在binlog日誌文件目錄下,我們還發現了這兩個文件:
image
事實上這就是我們接下來要講的redo log,也就是重做日誌,它是InnoDB引擎特有的,記錄了InnoDB引擎下的事務日誌。
2.1redo log作用
同樣,我們首先要搞明白的是,已經有binlog了,爲什麼還需要redo log。
因爲兩者分工不同。binlog主要用來做數據歸檔,但是它並不具備崩潰恢復的能力,也就是說如果你的系統突然崩潰,重啓後可能會有部分數據丟失,而redo log的存在則可以完美解決這個問題。
2.2redo log構成
默認情況下,每個InnoDB引擎至少有一個重做日誌組,每個組下至少有兩個重做日誌文件,例如上文提到的iblogfile0和iblogfile1。重做日誌組中的每個日誌文件大小一致且循環寫入,也就是說先寫iblogfile0,寫滿了之後寫iblogfile1,一旦iblogfile1也寫滿了,則繼續寫iblogfile0。顯然,如果沒有任何保護措施,這種機制會導致之前寫入ib_logfile0的內容被覆蓋。因此一旦redo log寫滿,MySQL將不得不停下所有更新操作來刷髒頁,這也是我們在爲什麼你的SQL執行很慢中提到的一點。
2.3redo log內容
binlog有三種格式,並且每種格式我們都可以具體查看其內容,那麼redo log的內容怎麼查看呢?
目前好像並沒有什麼辦法可以以人類可以理解的方式查看redo log,原因在於與binlog記錄的是邏輯日誌不同,redo log記錄的是對數據頁更改的物理日誌,比如類似「將第8頁、第1行、第6個位置改成666」這種,下圖是我用hexdump查看ib_logfile0的結果,其中紅框標出來的部分是插入一條數據後新增的日誌文件。
image
3.總結
本文從幾個方面簡單介紹了一下binlog和redo log,現在簡單總結一下兩者的不同點:
-
功能不同,binlog主要用於歸檔,而redo log主要用於崩潰恢復
-
內容不同,binlog是邏輯日誌,而redo log是物理日誌
-
寫入時機不同,binlog是server層記錄的,所有存儲引擎可共享,而redo log是InnoDB引擎特有的
-
寫入方式不同,binlog容量無限,追加寫入,而redo log容量有限,循環寫入