MySQLbinlog日誌03binlog日誌字節碼解析
本系列博客主要介紹MySQL數據庫的binlog日誌的相關內容,這個系列的主題包括:
MySQLbinlog日誌01binlog日誌基本操作
MySQLbinlog日誌02binlog日誌用於數據恢復
MySQLbinlog日誌03binlog日誌字節碼解析
MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件
本博客主要內容包括:
binlog事件類型
binlog事件頭部結構
binlog字節碼分析的準備工作
binlog日誌文件MAGIC代碼
Format desc事件
Table map事件
下一篇博客
MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件
將介紹:
Write rows事件
1. binlog事件類型
MySQL Server 5.7.22支持的事件類型如下所示,總共定義了38種。
START_EVENT_V3= 1,
QUERY_EVENT= 2,
STOP_EVENT= 3,
ROTATE_EVENT= 4,
INTVAR_EVENT= 5,
LOAD_EVENT= 6,
SLAVE_EVENT= 7,
CREATE_FILE_EVENT= 8,
APPEND_BLOCK_EVENT= 9,
EXEC_LOAD_EVENT= 10,
DELETE_FILE_EVENT= 11,
NEW_LOAD_EVENT= 12,
RAND_EVENT= 13,
USER_VAR_EVENT= 14,
FORMAT_DESCRIPTION_EVENT= 15,
XID_EVENT= 16,
BEGIN_LOAD_QUERY_EVENT= 17,
EXECUTE_LOAD_QUERY_EVENT= 18,
TABLE_MAP_EVENT = 19,
PRE_GA_WRITE_ROWS_EVENT = 20,
PRE_GA_UPDATE_ROWS_EVENT = 21,
PRE_GA_DELETE_ROWS_EVENT = 22,
WRITE_ROWS_EVENT_V1 = 23,
UPDATE_ROWS_EVENT_V1 = 24,
DELETE_ROWS_EVENT_V1 = 25,
INCIDENT_EVENT= 26,
HEARTBEAT_LOG_EVENT= 27,
IGNORABLE_LOG_EVENT= 28,
ROWS_QUERY_LOG_EVENT= 29,
WRITE_ROWS_EVENT = 30,
UPDATE_ROWS_EVENT = 31,
DELETE_ROWS_EVENT = 32,
GTID_LOG_EVENT= 33,
ANONYMOUS_GTID_LOG_EVENT= 34,
PREVIOUS_GTIDS_LOG_EVENT= 35,
TRANSACTION_CONTEXT_EVENT= 36,
VIEW_CHANGE_EVENT= 37,
XA_PREPARE_LOG_EVENT= 38,
對於使用binlog日誌文件進行數據恢復來說,下面4個事件非常重要:
TABLE_MAP_EVENT = 19, 將數據庫名和表名和表id關聯起來。
WRITE_ROWS_EVENT = 30, INSERT語句使用。
UPDATE_ROWS_EVENT = 31,UPDATE語句使用。
DELETE_ROWS_EVENT = 32,DELETE語句使用。
後面這三種事件處理方式非常類似,都是基於Rows_event,即包含記錄行數據。
1. binlog事件頭部結構
binlog事件具有相同的頭部結構,總共19個字節。下表中偏移和長度均為十進制整數。
偏移 |
長度 |
字段 |
0 |
4 |
時間戳 |
4 |
1 |
事件類型 |
5 |
4 |
MySQL server-id |
9 |
4 |
本事件的長度 |
13 |
4 |
下一個事件的開始位置 |
17 |
2 |
標誌 |
上面表格中偏移量13長度4字節的字段,在<<MySQL Internals Manual>>(2006)文件中描述如下:
事件頭部結構中的這個字段在MySQL Server 5.7.22源代碼中的註釋:
事件頭部結構在MyFlash工具的源代碼中定義如下(2019,適用於v4版本的binlog):
從後面的分析中可以看到,在MySQL Server 5.17..22版本產生的binlog日誌文件中,認為是下一個事件的開始位置更為合適。這個字段的作用主要是用於在整個文件中快速的遍歷各個事件從而找到特定的事件。
1. binlog字節碼分析的準備工作
本文的目標是通過解析MySQL數據庫的binlog日誌文件的字節碼來了解binlog日誌文件的格式。
目標binlog文件:mysql_binlog.000003
為了分析方便,復制為all.binlog,通過hexdump程序取得十六進制格式的文本文件all.hex,通過mysqlbinlog程序取得對應的sql文件。
[root@101 binlog]# ls -l all.*
-rw-r-----. 1 root root 10015 Sep 21 17:58 all.binlog
-rw-r--r--. 1 root root 49462 Sep 21 17:45 all.hex
-rw-r--r--. 1 root root 36776 Sep 21 17:43 all.sql
1. binlog日誌文件MAGIC代碼。
bin日誌文件的magic代碼的值在MySQL Server 5.7.22源代碼中的定義如下:
binlog日誌文件的前面4個字節是固定的magic代碼,內容為“.bin”。
1. Format desc事件
每個binlog日誌文件的第1個事件總是Format_desc。
mysql> show binlog events in ‘mysql_binlog.000003‘ limit 1\G
*************************** 1. row ***************************
Log_name: mysql_binlog.000003
Pos: 4
Event_type: Format_desc
Server_id: 101
End_log_pos: 123
Info: Server ver: 5.7.22-log, Binlog ver: 4
通過binlog文件的字節碼可以分析第1個事件的內容。
下面通過分析第1個事件的事件頭部結構。
時間戳:
mysql> select from_unixtime(0+0x5ba39a5c);
+-----------------------------+
| from_unixtime(0+0x5ba39a5c) |
+-----------------------------+
| 2018-09-20 21:02:20 |
+-----------------------------+
1 row in set (0.00 sec)
事件的字節數大小,以及事件的結束位置,以及下一個事件的開始位置:
mysql> select 0+0x77, 4+0x77 , 0+0x7b;
+--------+--------+--------+
| 0+0x77 | 4+0x77 | 0+0x7b |
+--------+--------+--------+
| 119 | 123 | 123 |
+--------+--------+--------+
1 row in set (0.00 sec)
因此,第1個事件的事件頭部結構的各個字段的值如下:
字段 |
字節碼 |
值 |
時間戳 |
5c9aa35b |
2018-09-20 21:02:20 |
事件類型 |
0f |
15 |
MySQL server-id |
65000000 |
101 |
本事件的長度 |
77000000 |
119 |
下一個事件的開始位置 |
7b000000 |
123 |
標誌 |
0000 |
0 |
這個事件的事件頭信息與前面show binlog events 的結果是一致的。
show binlog events 的結果中pos是這個事件的事件頭結構的開始位置,即在整個文件中的偏移量,第1個事件,因此偏移量總是4。 End_log_pos是這個事件的所有數據的最後一個字節之後的那個字節的位置,即下一個事件的事件頭結構的開始位置。
這個事件的長度是119,這個長度包括事件頭結構和事件具體內容兩個部分。
現在需要了解Format Desc事件的內容部分的具體格式:
在MySQL Server 5.7.22版本的源代碼中找到了以下的註釋:
似乎顯示binlog日誌是v4版本的,但是其中的基類的名字似乎顯示binlog日誌是v3版本的。
看到下面這段註釋終於明白了。MySQL Server V5.7.22的binlog日誌是V4版本的,只是這個Format_desc 事件是從Start_event_v3派生而來的。
至此,Format Desc事件的具體內容的格式已經清楚了:
由於事件的頭部結構的長度固定為19個字節,因此,此處的第1個字段的事件內部偏移為19。
事件偏移 |
長度 |
字段 |
19 |
2 |
binlog版本號 |
21 |
50 |
Server版本信息 |
71 |
4 |
創建時間戳 |
75 |
1 |
頭部結構的長度 |
76 |
N |
|
現在來觀察這個Format Desc事件的具體內容部分。
具體內容部分的開始位置和結束位置如下所示:
mysql> select hex(4+19),hex(4+119-1);
+-----------+--------------+
| hex(4+19) | hex(4+119-1) |
+-----------+--------------+
| 17 | 7A |
+-----------+--------------+
1 row in set (0.01 sec)
即下圖中灰色部分所示區域。
計算時間戳的偏移量:
mysql> select hex(4+71);
+-----------+
| hex(4+71) |
+-----------+
| 4B |
+-----------+
1 row in set (0.00 sec)
轉換為human-readable時間:
mysql> select from_unixtime(0x5ba39a5c);
+---------------------------+
| from_unixtime(0x5ba39a5c) |
+---------------------------+
| 2018-09-20 21:02:20 |
+---------------------------+
1 row in set (0.00 sec)
前面介紹的事件頭部結構(19個字節固定長度)對每個事件來說都是相同的結構相同的長度,稱之為common Header。
binlog事件的事件相關頭部的長度,事件相關頭部即具體某個事件特有的結構,稱之為Post Header。
事件類型的長度數組:
存儲本MySQL Server支持的每一種事件的Post Header Length,即事件相關頭部的長度。前面已經介紹過,總共38種類型的事件,因此這個數組總共更有38個元素,總共占用38個字節。
上圖中灰色標記區域即是這個數組的內容。
由於Format Desc事件的事件類型是15,因此這個事件的Post Header Length是0x5f,即十進制的95。可以這樣來驗證:
mysql> select 2+50+4+1+38;
+-------------+
| 2+50+4+1+38 |
+-------------+
| 95 |
+-------------+
1 row in set (0.00 sec)
上述元素的各項即Format Desc 事件的事件相關頭部的各個組成部分的長度。
因此,Format Desc事件的具體內容部分的各個字段的值總結如下:
字段 |
字節碼 |
值 |
binlog版本號 |
0400 |
4 |
Server版本信息 |
352e372e32322d6c6f6700 ..... |
5.7.22-log |
創建時間戳 |
5c9aa35b |
2018-09-20 21:02:20 |
頭部結構的長度 |
13 |
19 |
事件類型數組 |
省略 |
省略 |
前面已經了解這個事件的總長度是119個字節,剛剛介紹都是事件的頭部:Common Header和Post Header,總共95個字節。 可以發現實際數據顯示出多了5個字節:
mysql> select 19+95, 119-(19+95);
+-------+-------------+
| 19+95 | 119-(19+95) |
+-------+-------------+
| 114 | 5 |
+-------+-------------+
1 row in set (0.00 sec)
這5個字節的具體內容是什麽,稍後介紹。
下面進一步了解一下多出來的5個字節的真正的內容是什麽。
找到了以下這個代碼片段:
在95個字節之後還寫入了一個字節,這個字節用於指示是否使用校驗碼。這個字節的具體定義如下所示:
從前面的字節碼截圖可以看到,此處該值為1,即使用CRC32校驗。 在這個write函數中並沒有填充緊接著的4個字節,猜想有可能在其它地方處理了4個字節。
馬上看到了這個代碼片段:
其中定義了一個CRC32的簽名長度,正好4個字節。因此多出來的4個字節有可能是CRC32簽名的結果。
至此,Format Desc事件字節碼分析完畢。
Write rows事件將在下一篇博客中介紹。
MySQLbinlog日誌03binlog日誌字節碼解析