MySQLbinlog日志03binlog日志字节码解析
本系列博客主要介绍mysql数据库的binlog日志的相关内容,这个系列的主题包括:
mysqlbinlog日志04binlog日志字节码解析之二write_rows事件
前一篇博客介绍了
本篇博客将接着介绍write rows事件的字节码解析。
7.write rows事件
现在来解析insert语句对应的核心binlog事件:write rows事件。这个事件用于insert/update语句产生的增加和修改行数据的记录。每个write rows事件只涉及对一行数据的增加或者修改,尽管这个事件的名字用了复数形式。
对应的字节码数据如下所示:
common header的内容如下:
时间戳:
字段 |
字节码 |
值 |
时间戳 |
8191a35b |
2018-09-20 20:24:33 |
事件类型 |
1e |
30 |
mysql server-id |
65000000 |
101 |
本事件的长度 |
3b000000 |
59 |
下一个事件的开始位置 |
5e140000 |
5214 |
标志 |
0000 |
0 |
write rows事件的事件相关头结构格式还是从源代码注释中找到的,总体结构如下:
各个条目细分后的具体含义如下:
条目 |
长度 |
事件 偏移 |
备注 |
表id |
6 |
0 |
|
标志 |
2 |
6 |
|
var_header_len |
2 |
8 |
|
附加行数据 |
|
|
取决于var_header_len的值 |
列的个数 |
2 |
8 |
n:packed integer |
列标志 |
变长 |
|
int((n + 7) / 8,每个列1个bit。 |
操作前列数据标志 |
变长 |
|
定位行数据用到的列:insert/delete |
操作后列数据标志 |
变长 |
|
修改后的列:update,本例中没这部分。 |
行数据 |
变长 |
|
是否为null标志+列1的值+列2的值+... |
write rows事件自身的字节码数据如下:
表id占6个字节,值为120。
标志占2个字节,值为1。
var_header_len占2个字节,值为2字节。包含了自身的长度以及附加行数据的长度。因此这个事件中没有附加行数据。
接着是列的个数,这个整数的编码规则比较复杂。下面这个代码用于读取这样的列的个数的字节码。具体代码如下所示:
这个事件中第1个字节为06,因此列的数量就是6个列,仅仅占用1个字节。
接着是操作前的行记录用到的列的标志。对于insert语句而言,不包含这一部分。对于update/delete而言,就是mysqlbinlog输出的where中用到哪些列。因此具体格式在操作后的部分进行描述。
接着是操作后的行记录用到的列的标志。长度是(6+7)/8=1个字节,值为ff,只有6个二进制位有效。可以看到这个6个字段都被使用到了。这1个字节仅仅是标志位,不是实际的列数据。对于insert语句而言,就是新增的记录数据中包含哪些列。对于update而言,就是set中用到哪些列。
最后是真正的行记录的列数据。因为在common header中已经知道了整个事件的长度,而此时前面这些部分的长度也已经确定了,那么列数据的长度也可以计算出来。实际上就是分析到此时的偏移量开始,到事件结束位置的前1字节为止的这个范围内的字节码。
列数据具体是怎么存储的,稍后介绍。
行数据先经过pack_row()函数进行组装后才写入到binlog文件中。pack_row()函数的代码经过精简后如下所示:
先存储null标记,即值为null的列标记,每个列用一个位来表示其后的值是否为null。
t1表只有6个列,因此这里值为null的列只可能占用1个字节。当前事件中这个值为0xc0。
最后6位全部是0,这6个列当前的值全部不为null。
接着依次存储每个值不是null的列的具体数据。
每一种类型的列都有对应的pack()函数。前面已经知道了t1表的6个列中,第2列(name)是varchar之外,其它列都是int。int类型的列在mysql源代码中定义为field_long类。
功能主要就是这个32位整数存储为big endian格式的字节码,占用4个字节。
varchar类型的列对应的类是field_varstring。
对于varstring,先存储字符串的长度,再存储实际的字符串。整数类型的列的存储方式和前面介绍的各种长度的存储方式是类似的,都可以认为是big-endian格式,而这里的字符串长度,是按照little-endian格式存储的。小于255字节,存储为1个字节,否则存储为2个字节。
现在来解析这个write rows事件中包含的列的值,即行记录的具体数据。
列 |
字节码 |
值 |
null标记 |
c0 |
(11000000).6列的值均不为null |
第1列 |
0200 0000 |
int:2 |
第2列 |
024132 |
varstring:a2 |
第3列 |
1500 0000 |
int:21 |
第4列 |
1600 0000 |
int:22 |
第5列 |
1700 0000 |
int:23 |
第6列 |
1800 0000 |
int:24 |
这个结果与使用mysqlbinlog提取的结果是一致的:
最后面4个字节是这个binlog事件的校验码。
至此,insert语句对应的write row事件的字节码解析完毕。update和delete语句对应的binlog事件的字节码解析方式跟这个非常类似,就不再赘述了。
mysql数据库binlog系列博客就是这些内容了。