230_mysql_binlog结构组成
MySQL binlog event类型
https://dev.mysql.com/doc/internals/en/event-meanings.html
enum Log_event_type
{ UNKNOWN_EVENT= 0,
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,
ENUM_END_EVENT /* end marker */
};
1 常见binlog_event类型
event类型含义 |
|
FORMAT_DESCRIPTION_EVENT |
每个binlog文件开头的一个event, 记录binlog的版本,数据库版本,创建时间等基本信息 |
ROTATE_EVENT |
1切换新binlog文件:执行FLUSH LOGS语句 or binlog达阈值max_binlog_size意外宕机重启后会新生成binlog 2 切换新的binlog文件的时候,MySQL会在旧的binlog文件中写入一个ROTATE_EVENT,表示新的binlog文件的文件名,以及第一个偏移地址。 3宕机前最后一个binlog不是 ROTATE_EVENT结尾 |
数据库变更事件 |
|
TABLE_MAP_EVENT |
TABLE_MAP_EVENT只有在binlog文件是以ROW格式记录的时候,才会使用。binlog中记录的每个更改的记录之前都会有一个对应要操作的表的TABLE_MAP_EVENT。TABLE_MAP_EVENT中记录了表的定义(包括database name,table name,字段定义),并且会将这个表的定义对应于一个数字,称为table_id。设计TABLE_MAP_EVENT类型event的目的是为了当主库和从库之间有不同的表定义的时候,复制仍能进行。如果一个事务中操作了多个表,多行记录,在binlog中会将对多行记录的操作event进行分组,每组行记录操作event前面会出现对应表的TABLE_MAP_EVENT。 |
QUERY_EVENT |
QUERY_EVENT以文本的形式来记录事务的操作 QUERY_EVENT类型的事件通常在以下几种情况下使用: 事务开始时,执行的BEGIN操作。 STATEMENT格式中的DML操作。 ROW格式中的DDL操作。 |
ROWS_EVENT |
对于ROW格式的binlog,所有的DML语句都是记录在ROWS_EVENT中。 对于QUERY_EVENT事件,是以文本形式记录DML操作的。而对于ROWS_EVENT事件,并不是文本形式,所以在通过mysqlbinlog查看基于ROW格式的binlog时,需要指定-vv --base64-output=decode-rows |
WRITE_ROWS_EVENT UPDATE_ROWS_EVENT DELETE_ROWS_EVENT |
在以ROW格式记录的binlog文件中,WRITE_ROWS_EVENT记录了插入的行记录 在以ROW格式记录的binlog文件中,UPDATE_ROWS_EVENT记录了更新的行记录 在以ROW格式记录的binlog文件中,DELETE_ROWS_EVENT记录了删除的行记录 |
HEARTBEAT_LOG_EVENT |
由master发给slave的,让slave知道master还活着。这类事件不会在binlog或relay log中出现。 他们由master的dump事件线程产生,然后直接发给了slave。slave收到后,校验事件内容后,直接抛弃这个事件,而不会写到relay log中 |
2 event字节解析
在MySQL的发展过程中,一共出现过三个版本的event结构:
v1:在MySQL 3.23中使用 v2:在MySQL4.0.2至4.1中使用 v4:在MySQL5.0及以上版本中使用 |
3 event组织架构
v4版本的event主要由event header部分和post-header以及variable part部分组成。
event组织架构 |
|
event header |
是每个event都会有的部分,并且每个event header的格式是相同的,固定为19个字节长度,在event header 中记录了event_length、type_code等信息,可以表示event的长度,event的类型,下一个event的偏移位置等信息,都可以再event header中获取到。 |
extra_headers |
指定了除公共头部外的内容,但是目前版本的binlog格式中,这一部分不存在的 |
fixed_part & post-header |
每个类型的event之间的post-header部分是不相同的,但是同一类型的event占用的post-header 字节数是一样的。每个类型的event占用多少字节为post-header,这个定义在了Format_description_event中 |
variable part & body |
event真实记录具体信息的部分;part有些时候也被称作payload或者body |
event字节结构表示如下
timestamp记录的是该事务记录的时间 type_code,记录的是该event的类型,具体的event类型,见上面的Log_event_type server_id,记录的是执行该event的server的server_id号 event_length,该event的长度,共占多少字节 next_position,下一个event在binlog文件中的偏移位置 flag:标记 extra_headers,额外的头部内容,现在是空的,不存在 fixed part有些时候也被称作post-header variable part有些时候也被称作payload或者body |
event公共头部
从上述的event结构中,可以看到,每个event的header部分的内容结构是一致的,所有的event,都会记录这些信息,这是所有的event的公共头部分
字段 |
字节数 |
描述 |
timestamp |
4字节 |
时间戳 |
type_code |
1字节 |
记录event类型 |
server_id |
4 字节 |
记录event生成的MySQL所对应的server_id |
event_length |
4字节 |
这个event的字节长度,包括event header |
next_position |
4字节 |
下一个event在binlog文件中的偏移位置 |
flags |
2字节 |
|
b6 1e 1c 5a //timestamp:小端存储,将16进制转换成10进制为1511792310,将时间戳转换为时间为2017-11-27 22:18:30
1e //type_code:event类型,0x1e转换为10进制为30,查看Log_event_type中,看到30的为WRITE_ROWS_EVENT
0f 27 00 00 //server_id :小端存储,将16进制转换为10进制为9999
31 00 00 00 //event_length :小端存储,将16进制转换为10进制为49
b1 03 00 00 //next_position:小端存储,将16进制转换为10进制为945
00 00 //flags:
body部分省略
Format_description_event
Format_description_event是每个binlog文件开头的一个描述信息的event
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
binlog_version |
2字节 |
binlog结构的版本,v1,v2,v4 |
server_version |
50字节 |
MySQL server的数据库版本 |
timestamp |
4字节 |
创建该binlog文件的时间,单位为秒 |
header_length |
1字节 |
指定公共头部的长度,v4版本中这个值一直为19,说明其他所有的event的extra_header部分都是空, extra_header长度为header_length-19 |
event_post_header_length |
variable size跟各个版本支持的event类型总数一致 |
每个event类型,占用一个字节,表示该event类型post-header部分的长度 |
Variable part (event-body)部分
无
字节解析示例
公共头部部分省略
04 00 //binlog_version :v4版本
35 2e 36 2e 33 34 2d 6c 6f 67
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 //mysql server version :16进制转换为ascii之后,值为5.6.34-log
00 00 00 00 //timestamp:
13 //header length :19,说明该版本的event类型公共头部长度都为19,extra_header长度为header_length-19
38 0d 00 08 00 12 00 04 04 04
04 12 00 00 5c 00 04 1a 08 00
00 00 08 08 08 02 00 00 00 0a
0a 0a 19 19 00 //event_post_header_len:各个类型event的post-header部分长度
01 2e ef 23 e2
Rotate_event
当MySQL切换至新的binlog文件的时候,MySQL会在旧的binlog文件中写入一个ROTATE_EVENT,其内容包含新的binlog文件的文件名以及第一个偏移地址。当在数据库中执行FLUSH LOGS语句或者binlog文件的大小超过max_binlog_size参数设定的值就会切换新的binlog文件。
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
next_binlog_po |
8字节 |
下一个binlog文件起始的偏移地址 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
next_binlog_filename |
variable string |
下一个binlog文件的文件名 |
字节解析示例
公共头部部分省略
04 00 00 00 00 00 00 00 //next_binlog_pos:下一个binlog的起始偏移地址,小端存储,16进制转换为10进制之后为4
6d 79 73 71 6c 2d 62 69 6e 2e 30 30 30 30 30 32 //next_binlog_filename:下一个binlog的文件名,16进制转换为ascii之后,值为mysql-bin.000002
b3 db 0b 9a checksum
Query_log_event
记录更新操作的语句
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
thread_id |
4字节 |
执行语句的线程ID号 |
exec_time |
4字节 |
语句执行的时间 |
db_len |
1字节 |
database名的长度 |
error_code |
2字节 |
错误号 |
status_vars_len |
2字节 |
在v1和v3版本的event中是没有的,指定状态值的长度 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
status_vars |
status_vars_len字节 |
记录状态值,具体的解析见下表 |
database_name |
db_len+1字节 |
null-terminaled类型的字符串,记录database的名字 |
query_statement |
不定 |
执行的语句 |
check_sum |
4字节 |
校验码 |
status_vars的解析是,一个字节表示状态的类型,在类型之后按照类型不同紧接着不同字节数的状态值,状态的类型对应状态码以及字节数
enum Query_event_status_vars
{
Q_FLAGS2_CODE= 0, #4字节
Q_SQL_MODE_CODE= 1, #8字节
Q_CATALOG_CODE=2, #第一个字节表示catalog_len,总共catalog_len+2个字节
Q_AUTO_INCREMENT=3, #4字节
Q_CHARSET_CODE=4, #6字节
Q_TIME_ZONE_CODE=5, #第一个字节表示time_zone_len,总共time_zone_len+1字节
Q_CATALOG_NZ_CODE=6, # 第一个字节表示catalog_len,总共catalog_len+1个字节
Q_LC_TIME_NAMES_CODE=7, #2字节
Q_CHARSET_DATABASE_CODE=8, #2字节
Q_TABLE_MAP_FOR_UPDATE_CODE=9, #8字节
Q_MASTER_DATA_WRITTEN_CODE=10, #4字节
Q_INVOKER =11, #包含两部分,一部分是user,一部分是host。user部分,一个字节表示user_len,接着user_len个字节表示user。host部分,一个字节表示host_len,接着host_len个字节表示host
Q_UPDATED_DB_NAMES=12,
Q_MICROSECONDS =13, 3字节
Q_COMMIT_TS=14,
Q_COMMIT_TS2=15,
Q_EXPLICIT_DEFAULTS_FOR_TIMESTAMP=16 1字节
};
解析二进制文件
公共头部部分省略。。。。
a4 9f 01 00 //thread_id:执行语句的线程号,小端存储,转换为16进制为106404
00 00 00 00 //exec_time:执行语句的时间,小端存储,转化为16进制为0
08 //db_len:database name的长度,转换为10进制为8
00 00 //error_code:错误号,小端存储,转换为10进制为0
2a 00 //status_vars_len:状态值的长度,小端存储,转换为10进制为42,表示后续的42个字节为状态值的内容
//status_vars_len
00 00 00 00 00
01 00 00 20 40 00 00 00 00
06 03 73 74 64
03 02 00 02 00
04 21 00 21 00 53 00
0c 01 67 61 6e 67 73 68 65 6e 00
67 61 6e 67 73 68 65 6e 00 //database_name:数据库名称,null-terminaled string类型 ,转换为字符串为gangshen
696e7365727420696e746f20746573743128606e616d6560292076616c75657328276265696a696e672729 //query_statement:执行的语句,转换为字符串为:insert into test1(`name`) values('beijing')
21 92 37 98//checksum
Rows_query_log_event
在row格式复制模式下,将query以语句形式记录。在5.6.2版本之后,可以通过设置binlog_rows_query_log_events参数来控制在row格式复制模式下是否需要将query语句记录到binlog文件中。
fixed part (post-header) 部分
无
Variable part (event-body)部分
字段 |
字节数 |
描述 |
str_len |
1字节 |
记录语句长度 |
statement |
str_len字节 |
对应的语句 |
checksum |
4字节 |
校验码 |
解析二进制文件:
公共头部部分省略。。。
2e //str_len:执行语句的长度,转换为10进制为46
696e7365727420696e746f20746573743128606e616d6560292076616c7565732827726f77735f71756572792729 //statement:执行的语句,转换为ascii为insert into test1(`name`) values('rows_query')
8a af 68 dc //check_sum
Table_map_event
TABLE_MAP_EVENT只有在binlog文件是以ROW格式记录的时候,才会使用。binlog中记录的每个更改的记录之前都会有一个对应要操作的表的TABLE_MAP_EVENT。
TABLE_MAP_EVENT中记录了表的定义(包括database name,table name,字段定义),并且会将这个表的定义对应于一个数字,称为table_id。
设计TABLE_MAP_EVENT类型event的目的是为了当主库和从库之间有不同的表定义的时候,复制仍能进行。
如果一个事务中操作了多个表,多行记录,在binlog中会将对多行记录的操作event进行分组,每组行记录操作event前面会出现对应表的TABLE_MAP_EVENT
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
table_id |
6字节 |
操作的表的table_id |
flags |
2字节 |
目前版本没有用,都是0,保留给之后的版本使用 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
database_name |
1个字节表示字符串长度,之后接一个null-terminated类型的字符串 |
操作的数据库的名称
|
dtable_name |
1个字节表示字符串长度,之后接一个null-terminated类型的字符串 |
操作的表的名称 |
column_count |
packed integer(1或3或4或9个字节) |
对应表中的字段数量 |
column_type |
每个字段占用一个字节,字段类型定义在enum_field_types中 |
字段类型 |
metadata_length |
packed integer(1或3或4或9个字节) |
对应字段的元数据信息的长度 |
metadata |
根据enum_field_type中的定义确定不同字段的元数据,如果某字段类型没有元数据,则不记录 |
每个字段的元数据信息,比如varchar字段需要记录最长长度
|
null_bits |
int((column_count + 7)/8) 字节 |
一个bit表示一个字段是否可以为NULL,顺序是:第一个字节的最低位开始向最高位增长,之后第二个字节的最低位开始向最高位增长,以此类推
|
check_sum |
4字节 |
校验码 |
CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
二进制内容解析:
公共头部部分省略
6c 00 00 00 00 00 //table_id :小端存储,16进制转换为10进制为108
01 00 //flag :
08 67 61 6e 67 73 68 65 6e 00 //database_name :1个字节是字符串长度,后接一个null-terminated string 第一个字节表示字符串长度为8,后面内容将16进制转换为ascii字符为gangshen
05 74 65 73 74 31 00 //table_name: 1个字节是字符串长度,后接一个null-terminated string第一个字节表示字符串长度为5,后面内容将16进制转换为ascii字符为test1
02 //columns count :packet integer类型,转换之后,数值为2 表示表中有两个字段
03 0f //column type : 一个字节表示一个字段的类型,字段类型定义在enum enum_field_types ,分别是一个int类型以及一个varchar类型,具体的字段类型及记录格式等信息可以查看《MySQL中Rows_event中字段表示》文档
02 //metadata length : packet integer类型,转换之后,数值为2,表示记录表中的metadata内容占用2个字节
14 00 // :varchar 的max length 因为int没有metadata所以跳过
02 //null_bits :int((column_count + 7) / 8)个字节 一个bit表示一个字段是否可以为null
52 53 4a d9 checksum
Write_rows_log_event
在以ROW格式记录的binlog文件中,Write_rows_log_event记录了插入的行记录
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
table_id |
6字节 |
操作的表的table_id |
flags |
2字节 |
目前版本没有用,都是0,保留给之后的版本使用 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
var_header |
2字节 |
|
m_width |
packed integer(1或3或4或9个字节) |
表中字段数量 |
before_image |
(m_width + 7)/8字节 |
对应表中的字段数量 |
after_bitmap_bits |
(m_with +7)/8字节 |
字段值是否为空标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位增长,之后第二个字节的最低位开始向最高位增长,以此类推 |
after_column_content |
不定 |
按照顺序每个字段的内容 |
check_sum |
4字节 |
校验码 |
建表语句
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
插入数据:
insert into int_table values(1,11,111,1111,11111,true);
对应Write_rows_log_event使用mysqlbinlog解析
# at 340
#180103 10:21:20 server id 330619 end_log_pos 395 CRC32 0x50177e10 Write_rows: table id 100 flags: STMT_END_F
### INSERT INTO `gangshen`.`int_table`
### SET
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=11 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=111 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 395
公共头部部分省略
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //before image: (m_width + 7) / 8字节
c0 //after_bitmap_bits :insert插入之后的记录的NULL标记,表中六个字段,插入的值都不为NULL
//after_columns_content:insert插入记录之后的记录值
01
0b 00
6f 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
10 7e 17 50 //checksum
Update_rows_log_event
在以ROW格式记录的binlog文件中,Update_rows_log_event记录了更新的行记录。
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
table_id |
6字节 |
操作的表的table_id |
flags |
2字节 |
目前版本没有用,都是0,保留给之后的版本使用 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
var_header |
2字节 |
|
m_width |
packed integer(1或3或4或9个字节) |
表中字段数量 |
before_image |
(m_width + 7)/8字节 |
对应表中的字段数量 |
after_image _image |
(m_width + 7)/8字节 |
对应表中的字段数量 |
before_bitmap_bits |
(m_with+7)/8字节 |
before_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推 |
before_column_content |
不定 |
update更新之前的记录值,按照顺序每个字段的内容 |
after_bitmap_bits |
(m_with +7)/8字节 |
after_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推 |
after_column_content |
不定 |
update更新之后的记录值,按照顺序每个字段的内容 |
check_sum |
4字节 |
校验码 |
字节解析示例
建表语句
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
插入数据并更新:
insert into int_table values(1,11,111,1111,11111,true);
update int_table set col2=22,col3=222 where col1=1;
对应Update_rows_log_event使用mysqlbinlog解析
# at 644
#180103 10:41:37 server id 9999 end_log_pos 720 CRC32 0x5576b52f Update_rows: table id 231 flags: STMT_END_F
### UPDATE `gangshen`.`int_table`
### WHERE
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=11 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=111 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=22 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=222 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 720
解析二进制文件:
公共头部部分省略。。。
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //before image: (m_width + 7) / 8字节
ff //after image: (m_width + 7) / 8字节
c0 //before_bitmap_bits :update更新之前记录中NULL标记,表中六个字段,值都不为NULL
//before_column_content接下来是update更新之前记录的值
01
0b 00
6f 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
c0 //after_bitmap_bits :update更新之后记录中NULL标记,表中六个字段,值都不为NULL
//after_column_content接下来是update更新之前记录的值
01
16 00
de 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
16 a3 de bb //checksum
Delete_rows_log_event
在以ROW格式记录的binlog文件中,Delete_rows_log_event记录了删除的行记录。
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
table_id |
6字节 |
操作的表的table_id |
flags |
2字节 |
目前版本没有用,都是0,保留给之后的版本使用 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
var_header |
2字节 |
|
m_width |
packed integer(1或3或4或9个字节) |
表中字段数量 |
after_image _image |
(m_width + 7)/8字节 |
对应表中的字段数量 |
before_bitmap_bits |
(m_with+7)/8字节 |
before_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推 |
before_column_content |
不定 |
delete删除之前的记录值,按照顺序每个字段的内 |
check_sum |
4字节 |
校验码 |
建表语句
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
删除数据:
delete from int_table where col1=1;
对应Delete_rows_log_event使用mysqlbinlog解析
# at 320
#180103 14:24:54 server id 330619 end_log_pos 375 CRC32 0xce4818b2 Delete_rows: table id 100 flags: STMT_END_F
### DELETE FROM `gangshen`.`int_table`
### WHERE
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=22 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=222 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 375
解析二进制文件:
公共头部部分省略
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //after image: (m_width + 7) / 8字节
c0 //before_bitmap_bits :delete删除之前的记录的NULL标记,表中六个字段,值都不为NULL
//before_columns_content:delete删除记录之前的记录值
01
16 00
de 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
10 7e 17 50 //checksum
Xid_event
表示支持内部XA的存储引擎上的事务提交。正常的事务是通过QUERY_EVENT来发送一个BEGIN语句并且通过QUERY_EVENT来发送一个COMMIT语句(如果事务回滚则发送的是ROLLBACK)实现
fixed part (post-header) 部分
无
Variable part (event-body)部分
字段 |
字节数 |
描述 |
xid |
8字节 |
无符号数 |
after_image _image |
(m_width + 7)/8字节 |
对应表中的字段数量 |
# at 1691
#180103 15:30:45 server id 330619 end_log_pos 1722 CRC32 0x8816181c Xid = 2698
COMMIT/*!*/;
# at 1722
解析二进制文件:
公共头部部分省略...
8a 0a 00 00 00 00 00 00 //xid,因为是小端存储,所以实际为0x 00 00 00 00 00 00 0a 8a,转换为10进制为2698
1c 18 16 88 //check_sum
Gtid_log_event
开启GTID模式的场景下,每次事务commit提交时,MySQL会在binlog中事务开始写入一个Gtid_log_event,记录该事务的GTID事务号。
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
commit_flag |
1字节 |
标记事务是否提交,非0为已提交,为0表示,事务未提交 |
sid |
16字节 |
记录GTID中uuid的部分(不记录‘-’),每1个字节表示uuid中2个字符 |
gno |
8字节 |
小端存储,GTID中的事务号部分 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
checksum 4 |
4字节 |
校验码 |
字节解析示例
使用mysqlbinlog工具解析binlog文件
# at 191
#180109 18:31:08 server id 330619 end_log_pos 239 CRC32 0xd20330e8 GTID [commit=yes]
SET @@SESSION.GTID_NEXT= '89fbcea2-da65-11e7-a851-fa163e618bac:5'/*!*/;
# at 239
解析二进制文件:
公共头部部分省略。。。。
01 //commit_flag:标记事务是否已提交,表示已提交
89 fb ce a2 da 65 11 e7 a8 51 fa 16 3e 61 8b ac //sid:GTID中uuid部分,转换为uuid为89fbcea2-da65-11e7-a851-fa163e618bac
05 00 00 00 00 00 00 00 //gno:GTID中的事务号,小端存储,转换为10进制为5
e8 30 03 d2 //checksum
Previous_gtid_log_event
在开启GTID的模式下,每个binlog文件开始会记录一个Previous_gtid_log_event,Previous_gtid_log_event中会包含当前binlog之前所有binlog中的GTID集合
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
n_sids |
8字节 |
小端存储,记录之后有几个GTID中的uuid号 |
sid |
16字节 |
记录GTID中uuid的部分(不记录‘-’),每1个字节表示uuid中2个字符 |
n_intervals |
8字节 |
记录每个sid对应有几个间隔,指的是事务号间隔 |
start |
8字节 |
每个事务号间隔中的起始事务号 |
end |
8字节 |
每个事务号间隔中的结束事务号+1 |
Variable part (event-body)部分
字段 |
字节数 |
描述 |
checksum 4 |
4字节 |
校验码 |
字节解析示例
使用mysqlbinlog工具解析binlog文件
# at 120
#180111 14:10:27 server id 330619 end_log_pos 279 CRC32 0x94a92478 Previous-GTIDs
# 89fbcea2-da65-11e7-a851-fa163e618bac:1-5:999:1050-1052,
# aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1-2:5-7
DELIMITER ;
解析二进制文件:
公共头部部分省略。。。
02 00 00 00 00 00 00 00 //n_sids:记录之后有几个GTID中的uuid号,小端存储,转换为10进制为2,表后后续有2个uuid号
89 fb ce a2 da 65 11 e7 a8 51 fa 16 3e 61 8b ac //sid:GTID中的uuid号转换为uuid为89fbcea2-da65-11e7-a851-fa163e618bac
03 00 00 00 00 00 00 00 //n_intervals:对应的sid中中对应几个事务号间隔,小端存储,转换为10进制为3,表示有3个事务号间隔
01 00 00 00 00 00 00 00 //start:事务号间隔中的起始事务号,小端存储,转换为10进制为1
06 00 00 00 00 00 00 00 //end:事务号间隔中的结束事务号+1,小端存储,转换为10进制为6,实际的间隔结束事务号为5
e7 03 00 00 00 00 00 00 //start:事务号间隔中的起始事务号,小端存储,转换为10进制为999
e8 03 00 00 00 00 00 00 //end:事务号间隔中的结束事务号+1,小端存储,转换为10进制为1000,实际的间隔结束事务号为999
1a 04 00 00 00 00 00 00 //start:事务号间隔中的起始事务号,小端存储,转换为10进制为1050
1d 04 00 00 00 00 00 00 //end:事务号间隔中的结束事务号+1,小端存储,转换为10进制为1053,实际的间隔结束事务号为1052
aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa //sid:GTID中的uuid号转换为uuid为aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
02 00 00 00 00 00 00 00 //n_intervals:对应的sid中中对应几个事务号间隔,小端存储,转换为10进制为2,表示有2个事务号间隔
01 00 00 00 00 00 00 00 //start:事务号间隔中的起始事务号,小端存储,转换为10进制为1
03 00 00 00 00 00 00 00 //end:事务号间隔中的结束事务号+1,小端存储,转换为10进制为3,实际的间隔结束事务号为2
05 00 00 00 00 00 00 00 //start:事务号间隔中的起始事务号,小端存储,转换为10进制为5
08 00 00 00 00 00 00 00 //end:事务号间隔中的结束事务号+1,小端存储,转换为10进制为8,实际的间隔结束事务号为7
78 24 a9 94 //checksum
Anonymous_gtid_log_event
MySQL在binlog中记录每一个匿名事务之前会记录一个Anonymous_gtid_log_event表示接下来的事务是一个匿名事务。
注意:因为在5.6.34中并不会产生Anonymous_gtid_log_event,所以以下内容是基于5.7.19版本解析
fixed part (post-header) 部分
字段 |
字节数 |
描述 |
gtid_flags |
1字节 |
记录binlog格式,如果gtid_flags值为1,表示binlog中可能有以statement方式记录的binlog,如果为0表示,binlog中只有以row格式记录的binlog |
sid |
16字节 |
记录GTID中uuid的部分(不记录‘-’),每1个字节表示uuid中2个字符 |
gno |
8字节 |
GTID中的事务号部分 |
logical_timestamp_typecode |
1字节 |
判断是否有last_commit和sequence_no,在logical_tmiestamp_typecode=2的情况下,有last_commit和sequence_no |
last_commit |
8字节 |
上次提交的事务号 |
sequence_no |
8字节 |
本次提交的序列号
|
Variable part (event-body)部分
字段 |
字节数 |
描述 |
checksum 4 |
4字节 |
校验码 |
字节解析示例
使用mysqlbinlog工具解析binlog文件
#180109 10:53:54 server id 9999 end_log_pos 5681 CRC32 0x46be0639 Anonymous_GTID last_committed=20 sequence_number=21 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 5681
解析二进制文件:
公共头部部分省略。。。
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 //sid:
00 00 00 00 00 00 00 00 //gno:
02 //logical_timestamp_typecode:判断是否有last_commit和sequence_no,在logical_tmiestamp_typecode=2的情况下,有last_commit和sequence_no,所以这边的话,后续有last_commit和sequence_no部分的内容
14 00 00 00 00 00 00 00 //last_commit:上次提交的事务号,小端存储,转换为10机制为20
15 00 00 00 00 00 00 00 //sequence_no:本次提交的序列号,小端存储,转换为10进制为21
39 06 be 46 //checksum:校验码
- 点赞
- 收藏
- 关注作者
评论(0)