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 data和Variable 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é)一篇筆記。