MySQL binlog格式解析

binlog想必大家都不陌生,在主從復(fù)制或者某些情況下的數(shù)據(jù)恢復(fù)會(huì)用到。由于binlog是二進(jìn)制數(shù)據(jù),要查看一般都借助mysqlbinlog工具。這篇筆記分析了binlog格式,希望能夠了解下mysqlbinlog工具背后所做的事情。

1.什么時(shí)候?qū)慴inlog

在說(shuō)明什么時(shí)候?qū)慴inlog前,先簡(jiǎn)單介紹下binlog的用途。binlog是二進(jìn)制日志文件,用于記錄mysql的數(shù)據(jù)更新或者潛在更新(比如DELETE語(yǔ)句執(zhí)行刪除而實(shí)際并沒(méi)有符合條件的數(shù)據(jù)),在mysql主從復(fù)制中就是依靠的binlog。在mysql中開(kāi)啟binlog需要設(shè)置my.cnf中的log_bin參數(shù),另外也可以通過(guò)binlog_do_db
指定要記錄binlog的數(shù)據(jù)庫(kù)和binlog_ignore_db指定不記錄binlog的數(shù)據(jù)庫(kù)。對(duì)運(yùn)行中的mysql要啟用binlog可以通過(guò)命令SET SQL_LOG_BIN=1來(lái)設(shè)置。設(shè)置完成,我們就可以來(lái)測(cè)試binlog了。

需要注意下innodb引擎中的redo/undo log與mysql binlog是完全不同的日志,它們主要有以下幾個(gè)區(qū)別:

  • a)層次不同。redo/undo log是innodb層維護(hù)的,而binlog是mysql server層維護(hù)的,跟采用何種引擎沒(méi)有關(guān)系,記錄的是所有引擎的更新操作的日志記錄。innodb的redo/undo log更詳細(xì)的說(shuō)明可以參見(jiàn)姜承堯的《mysql技術(shù)內(nèi)幕-innodb存儲(chǔ)引擎》一書(shū)中相關(guān)章節(jié)。
  • b)記錄內(nèi)容不同。redo/undo日志記錄的是每個(gè)頁(yè)的修改情況,屬于物理日志+邏輯日志結(jié)合的方式(redo log物理到頁(yè),頁(yè)內(nèi)采用邏輯日志,undo log采用的是邏輯日志),目的是保證數(shù)據(jù)的一致性。binlog記錄的都是事務(wù)操作內(nèi)容,比如一條語(yǔ)句DELETE FROM TABLE WHERE i > 1之類的,不管采用的是什么引擎,當(dāng)然格式是二進(jìn)制的,要解析日志內(nèi)容可以用這個(gè)命令mysqlbinlog -vv BINLOG。
  • c)記錄時(shí)機(jī)不同。redo/undo日志在事務(wù)執(zhí)行過(guò)程中會(huì)不斷的寫(xiě)入;而binlog僅僅在事務(wù)提交后才寫(xiě)入到日志,之前描述有誤,binlog是在事務(wù)最終commit前寫(xiě)入的,多謝anti-semicolon 指出。當(dāng)然,binlog什么時(shí)候刷新到磁盤跟參數(shù)sync_binlog相關(guān)。

顯然,我們執(zhí)行SELECT等不涉及數(shù)據(jù)更新的語(yǔ)句是不會(huì)記binlog的,而涉及到數(shù)據(jù)更新則會(huì)記錄。要注意的是,對(duì)支持事務(wù)的引擎如innodb而言,必須要提交了事務(wù)才會(huì)記錄binlog。

binlog刷新到磁盤的時(shí)機(jī)跟sync_binlog參數(shù)相關(guān),如果設(shè)置為0,則表示MySQL不控制binlog的刷新,由文件系統(tǒng)去控制它緩存的刷新,而如果設(shè)置為不為0的值則表示每sync_binlog次事務(wù),MySQL調(diào)用文件系統(tǒng)的刷新操作刷新binlog到磁盤中。設(shè)為1是最安全的,在系統(tǒng)故障時(shí)最多丟失一個(gè)事務(wù)的更新,但是會(huì)對(duì)性能有所影響,一般情況下會(huì)設(shè)置為100或者0,犧牲一定的一致性來(lái)獲取更好的性能。

通過(guò)命令SHOW MASTER LOGS可以看到當(dāng)前的binlog數(shù)目。如下面就是我機(jī)器上的mysql的binlog情況,第一列是binlog文件名,第二列是binlog文件大小??梢酝ㄟ^(guò)設(shè)置expire_logs_days來(lái)指定binlog保留時(shí)間,要手動(dòng)清理binlog可以通過(guò)指定binlog名字或者指定保留的日期,命令分別是:purge master logs to BINLOGNAME;purge master logs before DATE;。

......
| mysql-bin.000018 |       515 |
| mysql-bin.000019 |       504 |
| mysql-bin.000020 |       107 |
+------------------+-----------+

2 binlog格式解析

2.1 binlog文件格式簡(jiǎn)介

binlog格式分為statement,row以及mixed三種,mysql5.5默認(rèn)的還是statement模式,當(dāng)然我們?cè)谥鲝耐街幸话闶遣唤ㄗh用statement模式的,因?yàn)闀?huì)有些語(yǔ)句不支持,比如語(yǔ)句中包含UUID函數(shù),以及LOAD DATA IN FILE語(yǔ)句等,一般推薦的是mixed格式。暫且不管這三種格式的區(qū)別,看看binlog的存儲(chǔ)格式是什么樣的。binlog是一個(gè)二進(jìn)制文件集合,當(dāng)然除了我們看到的mysql-bin.xxxxxx這些binlog文件外,還有個(gè)binlog索引文件mysql-bin.index。如官方文檔中所寫(xiě),binlog格式如下:

  • binlog文件以一個(gè)值為0Xfe62696e的魔數(shù)開(kāi)頭,這個(gè)魔數(shù)對(duì)應(yīng)0xfe 'b''i''n'。
  • binlog由一系列的binlog event構(gòu)成。每個(gè)binlog event包含header和data兩部分。
    • header部分提供的是event的公共的類型信息,包括event的創(chuàng)建時(shí)間,服務(wù)器等等。
    • data部分提供的是針對(duì)該event的具體信息,如具體數(shù)據(jù)的修改。
  • 從mysql5.0版本開(kāi)始,binlog采用的是v4版本,第一個(gè)event都是format_desc event 用于描述binlog文件的格式版本,這個(gè)格式就是event寫(xiě)入binlog文件的格式。關(guān)于之前版本的binlog格式,可以參見(jiàn)http://dev.mysql.com/doc/internals/en/binary-log-versions.html
  • 接下來(lái)的event就是按照上面的格式版本寫(xiě)入的event。
  • 最后一個(gè)rotate event用于說(shuō)明下一個(gè)binlog文件。
  • binlog索引文件是一個(gè)文本文件,其中內(nèi)容為當(dāng)前的binlog文件列表。比如下面就是一個(gè)mysql-bin.index文件的內(nèi)容。
/var/log/mysql/mysql-bin.000019
/var/log/mysql/mysql-bin.000020
/var/log/mysql/mysql-bin.000021

接下來(lái)分析下幾種常見(jiàn)的event,其他的event類型可以參見(jiàn)官方文檔。event數(shù)據(jù)結(jié)構(gòu)如下:

+=====================================+
| event  | timestamp         0 : 4    |
| header +----------------------------+
|        | type_code         4 : 1    |
|        +----------------------------+
|        | server_id         5 : 4    |
|        +----------------------------+
|        | event_length      9 : 4    |
|        +----------------------------+
|        | next_position    13 : 4    |
|        +----------------------------+
|        | flags            17 : 2    |
|        +----------------------------+
|        | extra_headers    19 : x-19 |
+=====================================+
| event  | fixed part        x : y    |
| data   +----------------------------+
|        | variable part              |
+=====================================+

2.2 format_desc event

下面是我在FLUSH LOGS之后新建的一個(gè)全新的binlog文件mysql-bin.000053,從binlog第一個(gè)event也就是format_desc event開(kāi)始分析(mysql日志是小端字節(jié)序):

root@ubuntu:/var/log/mysql# hexdump -C mysql-bin.000053

00000000  fe 62 69 6e b8 b2 7f 56  0f 04 00 00 00 67 00 00  |.bin...V.....g..|
00000010  00 6b 00 00 00 01 00 04  00 35 2e 35 2e 34 36 2d  |.k.......5.5.46-|
00000020  30 75 62 75 6e 74 75 30  2e 31 34 2e 30 34 2e 32  |0ubuntu0.14.04.2|
00000030  2d 6c 6f 67 00 00 00 00  00 00 00 00 00 00 00 00  |-log............|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 13  |................|
00000050  38 0d 00 08 00 12 00 04  04 04 04 12 00 00 54 00  |8.............T.|
00000060  04 1a 08 00 00 00 08 08  08 02 00                 |...........|

對(duì)照官方文檔中的說(shuō)明來(lái)看下format_desc event格式:

+=====================================+
| event  | timestamp         0 : 4    |
| header +----------------------------+
|        | type_code         4 : 1    | = FORMAT_DESCRIPTION_EVENT = 15
|        +----------------------------+
|        | server_id         5 : 4    |
|        +----------------------------+
|        | event_length      9 : 4    | >= 91
|        +----------------------------+
|        | next_position    13 : 4    |
|        +----------------------------+
|        | flags            17 : 2    |
+=====================================+
| event  | binlog_version   19 : 2    | = 4
| data   +----------------------------+
|        | server_version   21 : 50   |
|        +----------------------------+
|        | create_timestamp 71 : 4    |
|        +----------------------------+
|        | header_length    75 : 1    |
|        +----------------------------+
|        | post-header      76 : n    | = array of n bytes, one byte per event
|        | lengths for all            |   type that the server knows about
|        | event types                |
+=====================================+

前面4個(gè)字節(jié)是固定的magic number,值為0x6e6962fe。接著是一個(gè)format_desc event,先看下19個(gè)字節(jié)的header。這19個(gè)字節(jié)中前4個(gè)字節(jié)0x567fb2b8是時(shí)間戳,第5個(gè)字節(jié)0x0f是event type,接著4個(gè)字節(jié)0x00000004是server_id,再接著4個(gè)字節(jié)0x00000067是長(zhǎng)度103,然后的4個(gè)字節(jié)0x0000006b是下一個(gè)event的起始位置107,接著的2個(gè)字節(jié)的0x0001是flag(1為L(zhǎng)OG_EVENT_BINLOG_IN_USE_F,標(biāo)識(shí)binlog還沒(méi)有關(guān)閉,binlog關(guān)閉后,flag會(huì)被設(shè)置為0),這樣4+1+4+4+4+2=19個(gè)字節(jié)的公共頭就完了(extra_headers暫時(shí)沒(méi)有用到)。然后是這個(gè)event的data部分,event的data分為Fixed dataVariable data兩部分,其中Fixed data是event的固定長(zhǎng)度和格式的數(shù)據(jù),Variable data則是長(zhǎng)度變化的數(shù)據(jù),比如format_desc event的Fixed data長(zhǎng)度是0x54=84個(gè)字節(jié)。下面看下這84=2+50+4+1+27個(gè)字節(jié)的分配:開(kāi)始的2個(gè)字節(jié)0x0004為binlog的版本號(hào)4,接著的50個(gè)字節(jié)為mysql-server版本,如我的版本是5.5.46-0ubuntu0.14.04.2-log,與SELECT version();查看的結(jié)果一致。接下來(lái)4個(gè)字節(jié)是binlog創(chuàng)建時(shí)間,這里是0;然后的1個(gè)字節(jié)0x13是指之后所有event的公共頭長(zhǎng)度,這里都是19;接著的27個(gè)字節(jié)中每個(gè)字節(jié)為mysql已知的event(共27個(gè))的Fixed data的長(zhǎng)度;可以發(fā)現(xiàn)format_desc event自身的Variable data部分為空。

2.3 rotate event

接著我們不做額外操作,直接FLUSH LOGS,可以看到一個(gè)rotate event,此時(shí)的binlog內(nèi)容如下:

......
00000060  ................................. c2 b3 7f 56 04  |..............V.|
00000070  04 00 00 00 2b 00 00 00  96 00 00 00 00 00 04 00  |....+...........|
00000080  00 00 00 00 00 00 6d 79  73 71 6c 2d 62 69 6e 2e  |......mysql-bin.|
00000090  30 30 30 30 35 34                                 |000054|
00000096

前面的內(nèi)容跟之前的幾乎一致,除了format_desc event的flag從0x0001變成了0x0000。然后從0x567fb3c2開(kāi)始是一個(gè)rotate event。依照前面的分析,前面19個(gè)字節(jié)為event的header,其event type是0x04,長(zhǎng)度為0x2b=43,下一個(gè)event起始位置為0x96=150,然后是flag為0x0000,接著是event data部分,首先的8個(gè)字節(jié)為Fixed data部分,記錄的是下一個(gè)binlog的位置偏移4,而余下來(lái)的43-19-8=16個(gè)字節(jié)為Variable data部分,記錄的是下一個(gè)binlog的文件名mysql-bin.000054。對(duì)照mysqlbinlog -vv mysql-bin.000053可以驗(yàn)證。

ssj@ubuntu:/var/log/mysql$ mysqlbinlog -vv mysql-bin.000053 
...
# at 4
#151227 17:43:20 server id 4  end_log_pos 107   Start: binlog v 4, server v 5.5.46-0ubuntu0.14.04.2-log created 151227 17:43:20
BINLOG '
uLJ/Vg8EAAAAZwAAAGsAAAAAAAQANS41LjQ2LTB1YnVudHUwLjE0LjA0LjItbG9nAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
'/*!*/;
# at 107
#151227 17:47:46 server id 4  end_log_pos 150   Rotate to mysql-bin.000054  pos: 4
...

2.4 query event

刷新binlog,設(shè)置binlog_format=statement,創(chuàng)建一個(gè)表CREATE TABLEtt(ivarchar(100) DEFAULT NULL) ENGINE=InnoDB, 然后在測(cè)試表tt中插入一條數(shù)據(jù)insert into tt values('abc'),會(huì)產(chǎn)生3個(gè)event,包括2個(gè)query event和1個(gè)xid event。其中2個(gè)query event分別是BEGIN以及INSERT 語(yǔ)句,而xid event則是事務(wù)提交語(yǔ)句(xid event是支持XA的存儲(chǔ)引擎才有的,因?yàn)闇y(cè)試表tt是innodb引擎的,所以會(huì)有。如果是myisam引擎的表,也會(huì)有BEGIN和COMMIT,只不過(guò)COMMIT會(huì)是一個(gè)query event而不是xid event)。

mysql> show binlog events in 'mysql-bin.000060';
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------+
| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                   |
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------+
| mysql-bin.000060 |   4 | Format_desc |         4 |         107 | Server ver: 5.5.46-0ubuntu0.14.04.2-log, Binlog ver: 4 |
| mysql-bin.000060 | 107 | Query       |         4 |         175 | BEGIN                                                  |
| mysql-bin.000060 | 175 | Query       |         4 |         266 | use `test`; insert into tt values('abc')               |
| mysql-bin.000060 | 266 | Xid         |         4 |         293 | COMMIT /* xid=138 */                                   |
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------

binlog如下:

.......
0000006b  01 9d 82 56 02 04 00 00  00 44 00 00 00 af 00 00  |...V.....D......|
0000007b  00 08 00 26 00 00 00 00  00 00 00 04 00 00 1a 00  |...&............|
0000008b  00 00 00 00 00 01 00 00  00 00 00 00 00 00 06 03  |................|
0000009b  73 74 64 04 21 00 21 00  08 00 74 65 73 74 00 42  |std.!.!...test.B|
000000ab  45 47 49 4e                                       |EGIN|

000000af  01 9d 82 56 02 04 00 00  00 5b 00 00 00 0a 01 00  |...V.....[......|
000000bf  00 00 00 26 00 00 00 00  00 00 00 04 00 00 1a 00  |...&............|
000000cf  00 00 00 00 00 01 00 00  00 00 00 00 00 00 06 03  |................|
000000df  73 74 64 04 21 00 21 00  08 00 74 65 73 74 00 69  |std.!.!...test.i|
000000ef  6e 73 65 72 74 20 69 6e  74 6f 20 74 74 20 76 61  |nsert into tt va|
000000ff  6c 75 65 73 28 27 61 62  63 27 29                 |lues('abc')|

0000010a  01 9d 82 56 10 04 00 00  00 1b 00 00 00 25 01 00  |...V.........%..|
0000011a  00 00 00 8a 00 00 00 00  00 00 00                 |...........|

拋開(kāi)format_desc event,從0000006b開(kāi)始分析第一個(gè)query event。頭部跟之前的event一樣,只是query event的type為0x02,長(zhǎng)度為0x44=64,下一個(gè)event位置為0xaf=175。flag為8,接著是data部分,從format_desc event我們可以知道query event的Fixed data部分為13個(gè)字節(jié),因此也可以算出Variable data部分為64-19-13=32字節(jié)。

  • Fixed data:首先的4個(gè)字節(jié)0x00000026為執(zhí)行該語(yǔ)句的thread id,接下來(lái)的4個(gè)字節(jié)是執(zhí)行的時(shí)間0(以秒為單位),接下來(lái)的1個(gè)字節(jié)0x04是語(yǔ)句執(zhí)行時(shí)的默認(rèn)數(shù)據(jù)庫(kù)名字的長(zhǎng)度,我這里數(shù)據(jù)庫(kù)是test,所以長(zhǎng)度為4.接著的2個(gè)字節(jié)0x0000是錯(cuò)誤碼(注:通常情況下錯(cuò)誤碼是0表示沒(méi)有錯(cuò)誤,但是在一些非事務(wù)性表如myisam表執(zhí)行INSERT...SELECT語(yǔ)句時(shí)可能插入部分?jǐn)?shù)據(jù)后遇到duplicate-key錯(cuò)誤會(huì)產(chǎn)生錯(cuò)誤碼1062,或者是事務(wù)性表在INSERT...SELECT出錯(cuò)不會(huì)插入部分?jǐn)?shù)據(jù),但是在執(zhí)行過(guò)程中CTRL+C終止語(yǔ)句也可能記錄錯(cuò)誤碼。slave db在復(fù)制時(shí)會(huì)執(zhí)行后檢查錯(cuò)誤碼是否一致,如果不一致,則復(fù)制過(guò)程會(huì)中止),接著2個(gè)字節(jié)0x001a為狀態(tài)變量塊的長(zhǎng)度26。
  • Variable data:從0x001a之后的26個(gè)字節(jié)為狀態(tài)變量塊(這個(gè)暫時(shí)先不管),然后是默認(rèn)數(shù)據(jù)庫(kù)名test,以0x00結(jié)尾,然后是sql語(yǔ)句BEGIN,接下來(lái)就是第2個(gè)query event的內(nèi)容了。

第二個(gè)query event與第一個(gè)格式一樣,只是執(zhí)行語(yǔ)句變成了insert into tt values('abc')。

第三個(gè)xid event為COMMIT語(yǔ)句。前19個(gè)字節(jié)是通用頭部,type是16。data部分中Fixed data為空,而variable data為8個(gè)字節(jié),這8個(gè)字節(jié)0x000000008a是事務(wù)編號(hào)(注意事務(wù)編號(hào)不一定是小端字節(jié)序,因?yàn)槭菑膬?nèi)存中拷貝到磁盤的,所以這個(gè)字節(jié)序跟機(jī)器相關(guān))。

2.5 table_map event & write_rows event

這兩個(gè)event是在binlog_format=row的時(shí)候使用,設(shè)置binlog_format=row,然后創(chuàng)建一個(gè)測(cè)試表

CREATE TABLE `trow` (
  `i` int(11) NOT NULL,
  `c` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1`

執(zhí)行語(yǔ)句INSERT INTO trow VALUES(1, NULL), (2, 'a'),這個(gè)語(yǔ)句會(huì)產(chǎn)生一個(gè)query event,一個(gè)table_map event、一個(gè)write_rows event以及一個(gè)xid event。


mysql> show binlog events in 'mysql-bin.000074';
| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                   |
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------+
| mysql-bin.000074 |   4 | Format_desc |         4 |         107 | Server ver: 5.5.46-0ubuntu0.14.04.2-log, Binlog ver: 4 |
| mysql-bin.000074 | 107 | Query       |         4 |         175 | BEGIN                                                  |
| mysql-bin.000074 | 175 | Table_map   |         4 |         221 | table_id: 50 (test.trow)                               |
| mysql-bin.000074 | 221 | Write_rows  |         4 |         262 | table_id: 50 flags: STMT_END_F                         |
| mysql-bin.000074 | 262 | Xid         |         4 |         289 | COMMIT /* xid=245 */                                   

對(duì)應(yīng)的mysql-bin.000074數(shù)據(jù)如下:

...
#query event (BEGIN)
0000006b  95 2a 85 56 02 04 00 00  00 44 00 00 00 af 00 00  |.*.V.....D......|
0000007b  00 08 00 26 00 00 00 00  00 00 00 04 00 00 1a 00  |...&............|
0000008b  00 00 00 00 00 01 00 00  00 00 00 00 00 00 06 03  |................|
0000009b  73 74 64 04 21 00 21 00  08 00 74 65 73 74 00 42  |std.!.!...test.B|
000000ab  45 47 49 4e                                       |EGIN|

#table_map event
000000af  95 2a 85 56 13 04 00 00  00 2e 00 00 00 dd 00 00  |.*.V............|
000000bf  00 00 00 32 00 00 00 00  00 01 00 04 74 65 73 74  |...2........test|
000000cf  00 04 74 72 6f 77 00 02  03 0f 02 0a 00 02        |..trow........|

#write_rows event
000000dd  95 2a 85 56 17 04 00 00  00 29 00 00 00 06 01 00  |.*.V.....)......|
000000ed  00 00 00 32 00 00 00 00  00 01 00 02 ff fe 01 00  |...2............|
000000fd  00 00 fc 02 00 00 00 01  61                       |........a|

#xid event
00000106  95 2a 85 56 10 04 00 00  00 1b 00 00 00 21 01 00  |.*.V.........!..|
00000116  00 00 00 f5 00 00 00 00  00 00 00                 |...........|

0x0000006b-0x000000ae為query event,語(yǔ)句是BEGIN,前面已經(jīng)分析過(guò)。

table_map event

0x0000000af開(kāi)始為table_map event。除去頭部19個(gè)字節(jié),F(xiàn)ixed data為8個(gè)字節(jié),前面6個(gè)字節(jié)0x32=50為table id,接著2個(gè)字節(jié)0x0001為flags。

Variable data部分,首先1個(gè)字節(jié)0x04為數(shù)據(jù)庫(kù)名test的長(zhǎng)度,然后5個(gè)字節(jié)是數(shù)據(jù)庫(kù)名test+結(jié)束符。接著1個(gè)字節(jié)0x04為表名長(zhǎng)度,接著5個(gè)字節(jié)為表名trow+結(jié)束符。接著1個(gè)字節(jié)0x02為列的數(shù)目。而后是2個(gè)列的類型定義,分別是0x03和0x0f(列的類型MYSQL_TYPE_LONG為0x03,MYSQL_TYPE_VARCHAR為0x0f)。接著是列的元數(shù)據(jù)定義,首先0x02表示元數(shù)據(jù)長(zhǎng)度為2,因?yàn)镸YSQL_TYPE_LONG沒(méi)有元數(shù)據(jù),而MYSQL_TYPE_VARCHAR元數(shù)據(jù)長(zhǎng)度為2。接著的0x000a就是MYSQL_TYPE_VARCHAR的元數(shù)據(jù),表示我們?cè)诙x表時(shí)的varchar字段c長(zhǎng)度為10,最后一個(gè)字節(jié)0x02為掩碼,表示第一個(gè)字段i不能為NULL。關(guān)于列的類型以及元數(shù)據(jù)等更詳細(xì)的信息可以參見(jiàn)http://dev.mysql.com/doc/internals/en/table-map-event.html。

write_rows event

從0x000000dd開(kāi)始為write_rows event,除去頭部19個(gè)字節(jié),前6個(gè)字節(jié)0x32也是table id,然后兩個(gè)字節(jié)0x0001為flags。接著的1個(gè)字節(jié)0x02為表中列的數(shù)目。然后1個(gè)字節(jié)0xff各個(gè)bit標(biāo)識(shí)各列是否存在值,這里表示都存在。

接著的就是插入的各行數(shù)據(jù)了。第1個(gè)字節(jié)0xfe的各個(gè)bit標(biāo)識(shí)該行變化之后各列是否為NULL,為NULL記為1.這里表示第1列不為NULL,因?yàn)榈谝恍袛?shù)據(jù)插入的是(1,NULL)。接下來(lái)是各列的數(shù)據(jù),第一列是MYSQL_TYPE_LONG,長(zhǎng)度為4個(gè)字節(jié),所以0x00000001就是這個(gè)值。第二列是NULL不占字節(jié)。接下來(lái)是第二行,先是0xfc標(biāo)識(shí)兩列都不為NULL,先讀取第一列的4個(gè)字節(jié)0x00000002也就是我們插入的數(shù)字2,然后讀取第二列,先是一個(gè)字節(jié)的長(zhǎng)度0x01,然后是內(nèi)容0x61也就是字符'a'。到此,write_rows event也就分析完了。rows相關(guān)的event還有update_rows event和delete_rows event等,欲了解更多可以參見(jiàn)官方文檔。

最后是xid event,之前已經(jīng)分析過(guò),不再贅述。

2.6 intvar event

intvar event在binlog_format=statement時(shí)使用到,用于自增鍵類型auto_increment,十分重要。intval event的Fixed data部分為空,而Variable data部分為9個(gè)字節(jié),第1個(gè)字節(jié)用于標(biāo)識(shí)自增事件類型 LAST_INSERT_ID_EVENT = 1 or INSERT_ID_EVENT = 2,余下的8個(gè)字節(jié)為自增ID。
創(chuàng)建一個(gè)測(cè)試表 create table tinc (i int auto_increment primary key, c varchar(10)) engine=innodb;,然后執(zhí)行一個(gè)插入語(yǔ)句INSERT INTO tinc(c) values('abc');就可以看到intvar event了,這里的自增事件類型為INSERT_ID_EVENT。而如果用語(yǔ)句INSERT INTO tinc(i, c) VALUES(LAST_INSERT_ID()+1, 'abc'),則可以看到自增事件類型為L(zhǎng)AST_INSERT_ID_EVENT的intvar event。

| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                   |
+------------------+-----+-------------+-----------+-------------
| mysql-bin.000079 |   4 | Format_desc |         4 |         107 | Server ver: 5.5.46-0ubuntu0.14.04.2-log, Binlog ver: 4 |
| mysql-bin.000079 | 107 | Query       |         4 |         175 | BEGIN                                                  |
| mysql-bin.000079 | 175 | Intvar      |         4 |         203 | INSERT_ID=1                                            |
| mysql-bin.000079 | 203 | Query       |         4 |         299 | use `test`; insert into tinc(c) values('abc')          |
| mysql-bin.000079 | 299 | Xid         |         4 |         326 | COMMIT /* xid=263 */                                

3 簡(jiǎn)單總結(jié)

上面提到,binlog有三種格式,各有優(yōu)缺點(diǎn):

  • statement:基于SQL語(yǔ)句的模式,某些語(yǔ)句和函數(shù)如UUID, LOAD DATA INFILE等在復(fù)制過(guò)程可能導(dǎo)致數(shù)據(jù)不一致甚至出錯(cuò)。
  • row:基于行的模式,記錄的是行的變化,很安全。但是binlog會(huì)比其他兩種模式大很多,在一些大表中清除大量數(shù)據(jù)時(shí)在binlog中會(huì)生成很多條語(yǔ)句,可能導(dǎo)致從庫(kù)延遲變大。
  • mixed:混合模式,根據(jù)語(yǔ)句來(lái)選用是statement還是row模式。

不同版本的mysql在主從復(fù)制要慎重,雖然mysql5.0之后都用的V4版本的binlog了,估計(jì)還是會(huì)有些坑在里面,特別是高版本為主庫(kù),低版本為從庫(kù)時(shí)容易出問(wèn)題。在主從復(fù)制時(shí)最好還是主庫(kù)從庫(kù)版本一致,至少是大版本一致。mysql復(fù)制是個(gè)大的話題,希望有時(shí)間能單獨(dú)總結(jié)一篇筆記。

4 參考資料

http://dev.mysql.com/doc/internals/en/binary-log.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容