binlog淺析
一、基礎知識
什么是binlog?
(圖一)
全稱:Binary Log (二進制日志),包含描述數據庫更改的“ 事件 ”,例如表創建操作或對表數據的更改。二進制日志不用於諸如select或 show不修改數據的語句 。要記錄所有語句(例如,標識問題查詢),請使用常規查詢日志。
在哪里產生的?
我們都知道MYSQL有兩層結構,第一層:server層,里面包含連接器、查詢緩存、解析器、優化器、執行器,第二次是存儲引擎層,例如:InnoDB、MyISAM、Memory 等多個存儲引擎
(圖二)
binlog產生於mysql中的server層。
若是mysql采用的為innodb引擎(這里是經典的兩階段提交):
(圖三)
binlog文件都有什么?
binlog文件包含兩種類型:
- 索引文件(文件名后綴為.index)用於記錄哪些日志文件正在被使用
- 日志文件(文件名后綴為.00000*)記錄數據庫所有的DDL和DML(除了數據查詢語句)語句事件。
1.索引文件大小:我們可以通過 max_binlog_size 參數設置binlog文件的大小。Binlog最大值,最大和默認值是1GB,該設置並不能嚴格控制Binlog的大小,尤其是Binlog比較靠近最大值而又遇到一個比較大事務時,為了保證事務的完整性,不可能做切換日志的動作,只能將該事務的所有SQL都記錄進當前日志,直到事務結束
2.索引文件刪除:binlog的刪除可以手工刪除或自動刪除。通過設置 expire_logs_days 實現自動刪除
手動刪除需登錄mysql后執行如下命令:
mysql> reset master; //刪除master的binlog,即手動刪除所有的binlog日志 mysql> reset slave; //刪除slave的中繼日志 mysql> purge master logs before '2019-07-07 17:20:00'; //刪除指定日期以前的日志索引中binlog日志文件 mysql> purge master logs to 'binlog.000003'; //刪除指定日志文件的日志索引中binlog日志文件
可以通過如下命令確認目前正在使用binlog文件:
-- 通過這句話查詢到目前寫入的是哪個binlog文件 show master status;
binlog的格式都有什么?
(圖四)
binlog一共有三種格式:
- statement格式,最后有commit,記錄為基本語句
- row格式,記錄為基於行
- MIXED,日志記錄使用混合格式
format | 定義 | 優點 | 缺點 |
---|---|---|---|
statement | 記錄的是修改SQL語句 | 日志文件小,節約IO,提高性能 | 准確性差,對一些系統函數不能准確復制或不能復制,如now()、uuid()、limit(由於mysql是自選索引,有可能master同salve選擇的索引不同,導致更新的內容也不同)等 在某些情況下會導致master-slave中的數據不一致(如sleep()函數, last_insert_id(),以及user-defined functions(udf)等會出現問題) |
row | 記錄的是每行實際數據的變更 | 准確性強,能准確復制數據的變更 | 日志文件大,較大的網絡IO和磁盤IO |
mixed | statement和row模式的混合 | 准確性強,文件大小適中 | 當binlog format 設置為mixed時,普通復制不會有問題,但是級聯復制在特殊情況下會binlog丟失。 |
在MySQL 5.6中,默認的二進制日志記錄格式是 STATEMENT
。
日志格式也可以在運行時切換。存在兩種更改方式:
- mysql> SET GLOBAL binlog_format = 'STATEMENT'; 基於當前啟動的mysql的,若是當前mysql服務重啟,則日志格式又恢復為默認。
- mysql> SET SESSION binlog_format = 'STATEMENT'; 基於當前請求session的
優先級:session > global > default
我們測試一下日志格式有什么區別:
首先我們先確認下系統是否開啟了binlog ,系統為windows10系統,其中安裝的mysql版本是:mysql-5.7.20-winx64
然后登錄mysql 查看binlog是否開啟:show variables like 'log_bin';
我們本地安裝的mysql的默認的binlog都是關閉的,我們怎么打開它呢?
找到mysql安裝目錄下的my.ini文件,然后使用notepad++ 打開(可以使用其他編輯器),(這里注意了,我也使用txt文檔打開過,由於txt文檔打開的默認格式不是utf-8的,導致txt保存之后存在問題)。
增加如下內容:
# Binary Logging #MySQL 5.7.3 及以后版本,如果沒有設置server-id, 那么設置binlog后無法開啟MySQL服務 log-bin=E:/software/tool/environment/mysql/5.7.20/mysql-5.7.20-winx64/logs/binlogs #binlog日志格式,默認為STATEMENT:每一條SQL語句都會被記錄;ROW:僅記錄哪條數據被修改並且修改成什么樣子,是binlog開啟並且能恢復數據的關鍵; binlog-format=Row #binlog過期清理時間; expire_logs_days=7 #binlog每個日志文件大小; max_binlog_size=100m #binlog緩存大小; binlog_cache_size=4m #最大binlog緩存大小 max_binlog_cache_size=512m
#Server ID
server-id=201609
然后重啟mysql服務
- 確認下mysql是否開啟show variables like 'log_bin'; 對應的值是on就可以
- show variables like 'binlog_format' 看看目前我們的binlog日志是什么,因為我們在my.ini文件中設置了默認的binlog格式,這個時候我們看到的應該是row格式
- 執行
SET GLOBAL BINLOG_FORMAT = 'STATEMENT';
把更改后的客戶端的連接的
binlog_format
都更改為 STATEMENT 格式的 - 然后打開一個新的客戶端連接,確認下binlog_format是否更改,然后我們后面的操作會在這個連接中進行
- 創建一個表 test
CREATE TABLE `test` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `num` int(10) DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
並插入幾條數據
insert into test value(4,22);
- 然后我們查看下現在寫入的binlog日志文件是哪個
-- 然后看一下現在寫入的binlog文件 show master status;
我這里寫入的是binlogs.000003 ,如果是第一次使用的話,寫入的應該是binlogs.000001 。
- 然后可以跟蹤下日志的內容了
-- 然后看下記錄的內容 show binlog events in 'binlogs.000003';
我們看這里的info,從begin到commit,中間是真實執行的語句,我們實際上只是執行了insert操作,在這之前,還有use ...操作,這個命令不是我們主動執行的,而是mysql根據當前操作的數據表所在的庫,自動添加的
注意在最后有一個xid event,xid是把binlog和redolog關聯起來的關鍵,binlog和redolog都有一個共同的字段xid,當系統崩潰進行恢復的時候,會按照順序掃描binlog,若是碰到既有prepare又有commit的redolog,就直接提交;若是碰到只有prepare,而沒有commit的redolog,就直接拿xid去binlog查詢對應的事務。
這里簡單說下 redo Log,以順序的方式寫入文件,當全部文件寫滿的時候則回到第一個文件相應的起始位置進行覆蓋寫(但在做redo checkpoint時,也會更新第一個日志文件的頭部checkpoint標記,所以嚴格來講也不算順序寫),在InnoDB內部,邏輯上Redo Log被看作一個文件,對應一個space id (InnoDB通過space的概念來組織物理存儲,包括不同的表,數據字典,redo,undo等)。
來自於 https://blog.51cto.com/wangwei007/2287431、https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
- 那么我們再把binlog的日志格式更改為 row,看下效果。
可以看到 這種同 binlog格式為statement相比,前后的begin、commit是相同的, 但是row格式中的binlog沒有了sql語句的原文,而是替換成了兩個envent,Table_map和Write_rows ,table_map標識的是操作的表名,另外的ROWS_EVENT分為三種:WRITE_ROWS_EVENT,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT,分別對應insert,update和delete操作。
我們在這里完全看不出來具體操作內容是什么。我們需要輔助下mysqlbinlog工具。用這個命令解析和查看binlog中的內容。
我們可以看到這個事務是從459開始。
在這里我們可以看到server_id 是 201609 以及對應的xid = 182 以及對應的更新的值比如(@1=5,@2=22)最后的xid envent用於表示事務被正確的提交了。里面有一些參數暫未清楚。
- 再說下binlog的格式為mixed
出現的原因:
-
由於statement可能會導致主備不一致。
-
row格式的缺點是比較占用空間,若是使用一個delete刪掉10萬數據,使用statement就是一個sql語句,但是row格式的話,就要把10萬條記錄都寫到binlog中。這種不僅占用空間還耗費IO資源。
所以mysql就采取了個折中方案,也就是有了mixed的格式,mysql自己會判斷這條sql語句是否可能引起主備不一致,如果有可能,就用row格式,否則用statement格式。
-
為什么要寫binlog日志?
二進制日志有兩個重要目的:
-
主從復制,對於復制,主復制服務器上的二進制日志提供要發送到從屬服務器的數據更改的記錄。主服務器將其二進制日志中包含的事件發送到其從屬服務器,這些服務器執行這些事件以對主服務器上的數據進行相同的更改。
MySQL復制功能使用三個線程實現,一個在主服務器上,兩個在從服務器上:
-
-
Binlog轉儲線程。 主設備創建一個線程,以便在從設備連接時將二進制日志內容發送到從設備。這個線程就是
Binlog Dump
線程。二進制日志轉儲線程獲取主機二進制日志上的鎖,用於讀取要發送到從機的每個事件。一旦讀取了事件,即使在事件發送到從站之前,鎖也會被釋放。
-
從屬I / O線程。 在從屬服務器上發出語句時,從屬服務器會創建一個I / O線程,該線程連接到主服務器並要求它發送記錄在其二進制日志中的更新。
從屬I / O線程讀取主
Binlog Dump
線程發送的更新 (請參閱上一項)並將它們復制到包含從屬中繼日志的本地文件。 -
從屬SQL線程。 從屬設備創建一個SQL線程來讀取由從屬I / O線程寫入的中繼日志,並執行其中包含的事件。
-


-
某些數據恢復操作需要使用二進制日志。還原備份后,將重新執行備份后記錄的二進制日志中的事件。這些事件使數據庫從備份點更新。
1.假設執行的是delete語句,row格式下,binlog也會把delete的數據行保存起來,所以當執行完delete之后,發現刪錯了數據,可以直接把binlog中的delete轉為insert,被誤刪的數據就可以恢復了
2.如果是執行了insert語句,row格式下,binlog也會記錄insert的每一個字段,以及精確到剛剛插入的那一行,這個時候直接把insert轉為delete,刪掉這行數據就可以了
3.如果執行了update語句,row格式下,binlog里面會記錄修改前整行數據和修改后的整行數據,恢復update的話,只需要把這個envent前后的兩行信息對調一下,再去執行,就可以恢復了。
這里拿一個例子舉例:
若是我們線上數據在早晨8點做了全量備份,到了中午12點時,數據庫崩潰了(或者刪庫跑路了),這個時候我們應該怎么做呢?
- 首先我們要定位到數據庫當時記錄的binlog文件
- 其次定位到8點到中午十二點的binlog記錄
- 把數據庫的全量備份還原,並把binlog記錄導出到sql文件
- 執行binlog導出的sql文件,即能夠恢復到當時的數據狀態。
-- 通過這句話查詢到目前寫入的是哪個binlog文件 show master status; -- 查看binlog的日志記錄 show binlog events in 'binlogs.000002'; -- 在binlogs目錄下 導出update記錄 -- mysqlbinlog --start-position=2388 --stop-position=2699 binlogs.000002 > e:\\update2.sql -- 登錄mysql 執行數據恢復 -- source e://update.sql
關於binlog的簡單介紹就先說到這里了。加油!
參考文章:
圖一:https://dev.mysql.com/doc/refman/5.6/en/binary-log.html
圖二:https://zhaodengfeng1989.iteye.com/blog/2419768
圖三:https://www.jianshu.com/p/4bcfffb27ed5
https://www.cnblogs.com/rjzheng/p/9721765.html
https://dev.mysql.com/doc/refman/5.6/en/binary-log.html
https://www.cnblogs.com/ivictor/p/5780617.html
https://blog.csdn.net/bohu83/article/details/81568341