天天看點

MySQL Binlog解析

MySQL Server 有四種類型的日志——Error Log、General Query Log、Binary Log 和 Slow Query Log。

第一個是錯誤日志,記錄 mysqld 的一些錯誤。第二個是一般查詢日志,記錄 mysqld 正在做的事情,比如用戶端的連接配接和斷開、來自用戶端每條 Sql Statement 記錄資訊;如果你想準确知道用戶端到底傳了什麼瞎 [哔哔] 玩意兒給服務端,這個日志就非常管用了,不過它非常影響性能。第四個是慢查詢日志,記錄一些查詢比較慢的 SQL 語句——這種日志非常常用,主要是給開發者調優用的。

剩下的第三種就是 Binlog 了,包含了一些事件,這些事件描述了資料庫的改動,如建表、資料改動等,也包括一些潛在改動,比如 DELETE FROM ran WHERE bing = luan,然而一條資料都沒被删掉的這種情況。除非使用 Row-based logging,否則會包含所有改動資料的 SQL Statement。

那麼 Binlog 就有了兩個重要的用途——複制和恢複。比如主從表的複制,和備份恢複什麼的。

顯然,我們執行SELECT等不設計資料變更的語句是不會記錄Binlog的,而涉及到資料更新則會記錄。要注意的是,對支援事務的引擎如InnoDB而言,必須要送出了事務才會記錄Binlog。Binlog是在事務最終commit前寫入的,binlog什麼時候重新整理到磁盤跟參數sync_binlog相關。如果設定為0,則表示MySQL不控制binlog的重新整理,由檔案系統去控制它緩存的重新整理,而如果設定為不為0的值則表示每sync_binlog次事務,MySQL調用檔案系統的重新整理操作重新整理binlog到磁盤中。設為1是最安全的,在系統故障時最多丢失一個事務的更新,但是會對性能有所影響,一般情況下會設定為100或者0,犧牲一定的一緻性來擷取更好的性能。

通過配置/etc/my.cnf配置檔案的log-bin選項:

這個需要重新開機MySQL服務。

可以使用SET SQL_LOG_BIN=0指令停止使用日志檔案,然後可以通過SET SQL_LOG_BIN=1指令來啟用。

除了以上方法之外,也可以采用作業系統的本地指令删除檔案。

當遇到以下3種情況時會重新生成一個新的日志檔案,檔案序号遞增:

MySQL伺服器停止或重新開機時,MySQL會在重新開機時生成一個新的日志檔案;

使用flush logs指令;

當binlog檔案大小超過max_binlog_size系統變量配置的上限時;

binlog檔案的最大值和預設值是1GB,該設定并不能嚴格控制binlog的大小,尤其是binlog比較靠近最大值而又遇到一個比較大事務時,為了保證事務的完整性,不可能做切換日志的動作,隻能将該事務的所有SQL都記錄到目前日志,直到事務結束。

binlog有三種格式:Statement, Row和Mixed.

基于SQL語句的複制(statement-based replication, SBR)

基于行的複制(row-based replication, RBR)

混合模式複制(mixed-based replication, MBR)

Statement

每一條會修改資料的sql都會記錄在binlog中。

優點:不需要記錄每一行的變化,減少了binlog日志量,節約了IO, 提高了性能。

缺點:由于記錄的隻是執行語句,為了這些語句能在slave上正确運作,是以還必須記錄每條語句在執行的時候的一些相關資訊,以保證所有語句能在slave得到和在master端執行的時候相同的結果。另外mysql的複制,像一些特定函數的功能,slave可與master上要保持一緻會有很多相關問題。

相比row能節約多少性能與日志量,這個取決于應用的SQL情況,正常同一條記錄修改或者插入row格式所産生的日志量還小魚statement産生的日志量,但是考慮到如果帶條件的update操作,以及整表删除,alter表等操作,row格式會産生大量日志,是以在考慮是否使用row格式日志時應該根據應用的實際情況,其所産生的日志量會增加多少,以及帶來的IO性能問題。

Row

5.1.5版本的MySQL才開始支援row level的複制,它不記錄sql語句上下文相關資訊,僅儲存哪條記錄被修改。

優點: binlog中可以不記錄執行的sql語句的上下文相關的資訊,僅需要記錄那一條記錄被修改成什麼了。是以row的日志内容會非常清楚的記錄下每一行資料修改的細節。而且不會出現某些特定情況下的存儲過程,或function,以及trigger的調用和觸發無法被正确複制的問題.

缺點:所有的執行的語句當記錄到日志中的時候,都将以每行記錄的修改來記錄,這樣可能會産生大量的日志内容。

新版本的MySQL中對row level模式也被做了優化,并不是所有的修改都會以row level來記錄,像遇到表結構變更的時候就會以statement模式來記錄,如果sql語句确實就是update或者delete等修改資料的語句,那麼還是會記錄所有行的變更。

Mixed

從5.1.8版本開始,MySQL提供了Mixed格式,實際上就是Statement與Row的結合。

在Mixed模式下,一般的語句修改使用statment格式儲存binlog,如一些函數,statement無法完成主從複制的操作,則采用row格式儲存binlog,MySQL會根據執行的每一條具體的sql語句來區分對待記錄的日志形式,也就是在Statement和Row之間選擇一種。

檢視目前伺服器使用的二進制檔案及大小:

顯示主伺服器使用的二進制檔案及大小:

顯示目前使用的二進制檔案及所處位置:

binlog_do_db:此參數表示隻記錄制定資料庫的二進制日志 binlog_ignore_db:此參數标示不記錄指定的資料庫的二進制日志

需要檢視某個具體binlog檔案的内容時,以“mysql-bin.000005”為例。

在MySQL用戶端輸入:show binlog events in “mysql-bin.000005”;

效果如下所示:

或者使用mysqlbinlog指令,在shell終端輸入(假設目前目錄為 ../mysql/data): ../bin/mysqlbinlog mysql-bin.000005

截取上面的一段進行分析:

上面輸出包括如下要素:

position: 位于檔案中的位置,即第一行的(# at 314),說明該事件記錄從檔案第314個位元組開始

timestamp: 事件發生的時間戳,即第二行的(#161020 11:07:29)

exec_time: 事件執行的花費時間

error_code: 錯誤碼

server id: 伺服器辨別(2)

thread_id: 代理線程id (thread_id=162)

type:事件類型Query(參考下一章節)

使用mysqlbinlog指令還可以進行資料庫恢複,使用日志進行恢複時,需要依次進行,即最早生成的日志檔案要最先恢複:

binlog事件類型一共有三個版本:

v1: Used in MySQL 3.23

v3: Used in MySQL 4.0.2 though 4.1

v4: Used in MySQL 5.0 and up

v2出現了很短的時間,并且已經不被支援

現在所使用的MySQL一般都是5.5起了,是以下面陳述的都是v4版的binlog事件類型。

binlog的事件類型一共有以下幾種:

20-22三個類型現在已經被飛起,這三個類型隻出現在MySQL的5.1.5-5.1.7版本中。現在(從5.1.7版本開始)使用的WRITE, UPDATE, DELETE三個事件類型分别采用23,24,25。

QUERY_EVENT

記錄一條query語句,在基于語句的複制和基于行的複制都會有。

ROTATE_EVENT

二進制日志更換一個新檔案,可能因為檔案大小達到限制,或者是mysql重新開機,亦或者是調用了flush logs指令。

XID_EVENT

Commit事件

WRITE_ROWS_EVENT, UPDATE_ROWS_EVENT, DELETE_ROWS_EVENT

統稱為ROW EVENT, 隻有在基于row的複制方式下才會産生。

WRITE_ROWS_EVENT:包含了要插入的資料

UPDATE_ROWS_EVENT:包含了修改前的值,也包含了修改後的值

DELETE_ROWS_EVENT:包含了需要删除行前的值

TABLE_MAP_EVENT

ROW EVENT之前産生,為的是對ROW EVENT解析提供依據。

FORMAT_DESCRIPTION_EVENT

MySQL根據其定義來解析其他事件

INTVAR_EVET

在statement時使用到,用于自增類型auto_increment.

STOP_EVENT

MySQL停止時,在檔案尾加入STOP_EVENT

每個event都有一個19個位元組的Binlog Event Header(如下圖), 包括四個位元組的timestamep, 一個位元組的Binlog Event type, 4個位元組的server_id(該id表明binlog的源server是哪個,用來在循環複制中國龍event),四個位元組的event包大小,四個位元組的下一個event起始偏移,兩個位元組的Binlog Event Flag. extra_headers目前的event中沒有涉及到,預留用。

V4 event structure:

MySQL Binlog解析

一個新的binlog檔案都是以FORMAT_DESCRIPTION_EVENT開始的(v4版)。這裡就以FORMAT_DESCRIPTION_EVENT為例來解析下(mysql-bin.000005)。通過hexdump -C mysql-bin.000005來檢視二進制檔案。(下面隻列出部分内容)

按照官方文檔中的說明來看下FORMAT_DESCRIPTION_EVENT格式:

v4 format description event

MySQL Binlog解析

binlog是小端位元組序的。binlog前4個位元組是魔數:0xFE 0x62 0x69 0x6E. 接着是一個FORMAT_DESCRIPTION_EVENT,先看下19個位元組的event header. f1 34 08 58即0x580834f1是指時間戳,占4個位元組;第5個位元組0x0f是type_code即event type(FORMAT_DESCRIPTION_EVENT=15);接着4個位元組02 00 00 00 即0x00000002是server_id;再接着4個位元組67 00 00 00是event_length=0x00000067=103;然後4個位元組6b 00 00 00是下一個next_position=0x0000006b=107;接着兩個位元組01 00是flag=0x0001=1,1為LOG_EVENT_BINLOG_IN_USE_F,辨別binlog還沒有關閉,binlog關閉後,flag會被設定為0。這樣4+1+4+4+4+2=19個位元組。

event data部分分為fixed data和variable data兩部分,其中fixed data是event的固定長度和格式的資料,variable data則是長度變化的資料,比如FORMAT_DESCRIPTION_EVENT的fix data長度是0x54=84位元組。下面看下這84=2+50+4+1+27個位元組的配置設定:開始的2個位元組0x0004是binlog的版本号;接着的50個位元組為mysql-server版本5.5.51-log;接下來4個位元組是binlog建立時間,這裡是0;然後1個位元組是0x13是指之後所有event的公共長度,這裡都是19;接着27個位元組中每個位元組為mysql已知的event(共27個)的fixed data的長度;可以發現FORMAT_DESCRIPTION_EVENT自身的variable data部分為空。

參考資料: