MySQLbinlog日志03binlog日志字节码解析
Posted coe2coe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQLbinlog日志03binlog日志字节码解析相关的知识,希望对你有一定的参考价值。
本系列博客主要介绍mysql数据库的binlog日志的相关内容,这个系列的主题包括:
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文件。
[[email protected] 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 1G
*************************** 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日志字节码解析的主要内容,如果未能解决你的问题,请参考以下文章