至此,19个字节长度的 event header 解析就完成了。最重要的信息当属 event type ,因为我们要根据这个type信息才能继续下面的 event body 解析。不同类型的event, event header 布局相同,但是 event body 却不同,自然就要区别对待了。
TABLE_MAP_EVENT的event body
在解析 event header 时,获得了 event type 。根据 event type 来解析不同的event。这个 event type 在源码binlog_event.h文件中定义,其中 TABLE_MAP_EVENT = 19,UPDATE_ROWS_EVENT = 31。
TABLE_MAP_EVENT的布局格式参考: https://dev.mysql.com/doc/internals/en/table-map-event.html
代码中判断如果是 TABLE_MAP_EVENT ,就按照既定格式解析。很简单的判断:
if eventType == 19 {
......
}
复制代码
在这个判断中,增加解析逻辑。event body开头就是6个字节长度的table id ,这个值与我们执行
mysql> show binlog events in 'greatdb-bin.000001';
接下来是相对比较复杂的 column_count 信息,也就是执行的语句涉及了多少表中的列。首先是1个字节长度的 lenenc-int 位,就是通过这1个字节的值来判断后面使用了多长字节来存储 column_count 值。
如果 lenenc-int 位数值小于251,则 column_count 通过1个字节存储;
如果 lenenc-int 位数值小于216,则 column_count 通过 1 + 2 = 3 个字节存储;
如果 lenenc-int 位数值小于224,则 column_count 通过 1 + 3 = 4 个字节存储;
如果 lenenc-int 位数值小于264,则 column_count 通过 1 + 8 = 9 个字节存储;
参考官方文档的描述(地址:https://dev.mysql.com/doc/internals/en/integer.html#packet-Protocol::LengthEncodedInteger )
An integer that consumes 1, 3, 4, or 9 bytes, depending on its numeric value
To convert a number value into a length-encoded integer:
If the value is < 251, it is stored as a 1-byte integer.
If the value is ≥ 251 and < ( 216 ), it is stored as fc + 2-byte integer.
If the value is ≥ (216) and < (224), it is stored as fd + 3-byte integer.
If the value is ≥ (224) and < (264) it is stored as fe + 8-byte integer.
下面就是解析获得 column_count 的代码逻辑:
接下来是 column_meta_def 信息,MySQL文档的描述是: column_meta_def (lenenc_str) -- array of metainfo per column, length is the overall length of the metainfo-array in bytes, the length of each metainfo field is dependent on the columns field type
column_meta_def 记录了表的列的类型的元数据(通常为列的长度和精度),有些列类型没有元数据,有些类型有元数据,根据类型不同,有的用1个字节记录,有的用2个字节记录。
例如: MYSQL_TYPE_NEWDECIMAL(0xf6)有2个字节的元数据,第一个字节用于记录长度(precision), 第二个字节用于记录精度(scale): decimal(8,2) meta_def = 0x0802。这个信息暂时用不到,不做详细解析。
for _, v := range columnDef {
if v == "MYSQL_TYPE_STRING" || v == "MYSQL_TYPE_VAR_STRING" || v == "MYSQL_TYPE_VARCHAR" || v == "MYSQL_TYPE_DECIMAL" || v == "MYSQL_TYPE_NEWDECIMAL" || v == "MYSQL_TYPE_ENUM" {
pos += 2
} else if strings.Contains(v, "int") || strings.Contains(v, "bit") || v == "date" {
continue
} else if v == "MYSQL_TYPE_BLOB" || v == "MYSQL_TYPE_DOUBLE" || v == "MYSQL_TYPE_FLOAT" {
各列的bit位都是1,说明修改语句涉及了所有列。因为只有4列,剩余bit位没有用到,用1填充。
接下来也就是真正存储的行数据了:rows。修改前的数据和修改后的数据。
首先是修改前数据的 nul-bitmap ,也就是更新涉及的那些列的值是null。文档描述它的长度是:nul-bitmap, length (bits set in 'columns-present-bitmap1'+7)/8