TABLE_MAP_EVENT用于描述即将发生变化的表的结构。当用户提交一条修改语句时(如, insert, update, delete),MySQL会产生2个Binlog事件: 第一个就是TABLE_MAP_EVENT,用于描述改变对应表的结构(表名, 列的数据类型等信息);紧接着的是ROWS_EVENT,用于描述对应表的行的变化值,后续会接着介绍。

先看TABLE_MAP_EVENT的官方定义:The TABLE_MAP_EVENT defines the structure if the tables that are about to be changed.

post-header:
   if post_header_len == 6 {
     4              table id
   } else {
     6              table id
   }
   2              flags
payload:
   1              schema name length
   string         schema name
   1              [00]
   1              table name length
   string         table name
   1              [00]
   lenenc-int     column-count
   string.var_len [length=$column-count] column-def
   lenenc-str     column-meta-def
   n              NULL-bitmask, length: (column-count + 8) / 7

1. table id

用于唯一标识一个表的结构,table id是MySQL自动递增分配的。需要注意的是,table id唯一标识的是一个表结构,而不是实际的数据库表。如,一个普通表tpcc.my_test1, 如果在MySQL实例运行期间,没有发生结构变化(DML),那么它的table id就不会变化;但结构发生变化后,会生成新的TABLE_MAP_EVENT,并且使用新的table id标识。

table id是数值类型,根据TABLE_MAP_EVENT的post_header的长度不同占用的字节数也不同,如果post_header的长度为6,那么table_id占用4个字节,否者占6个字节。post_header的长度可以通过前面提到的FORMAT_DESCRIPTION_EVENT获取,这里不赘诉了。

2.flags

占2个字节,保留给未来使用,不需要关心。

3. schema length and name

这里记录了对应表的schema名,首先通过1个字节记录schema名的长度,然后根据长度,就可以获取schema名,并以null[0x00]结尾。

4.table length and name

这里记录了对应表的表名,首先通过1个字节记录表名的长度,然后根据长度,就可以获取表名,并以null[0x00]结尾。

5. column-count

记录了对应表有多少列,lenenc-int类型,也就是Length-Encoded-Integer,可以参考文档https://dev.mysql.com/doc/internals/en/integer.html#packet-Protocol::LengthEncodedInteger。

简单来说,是一种可变长的存储数值的协议,具体实现来说:

根据第一个字节的数值来确定该数值的存储字节数:
如果第一个字节数值小于0xfb,则该数值通过1个字节存储;
如果第一个字节数值等于0xfc,则该数值通过2个字节存储;
如果第一个字节数值等于0xfd,则该数值通过3个字节存储;
如果第一个字节数值等于0xfe,则该数值通过8个字节存储;
先读取第一个字节,根据第一个字节的值,来获取column-count有多少个字节。

6. column-def

记录了表的列的类型,每一列占1个字节,总共column-count个字节。如:
0x04对应MYSQL_TYPE_FLOAT,0x05对应MYSQL_TYPE_DOUBLE,0xfc对应MYSQL_TYPE_BLOB等。

7. column_meta_def

记录了表的列的类型的元数据(通常为列的长度和精度),有些列类型没有元数据,有些类型有元数据,根据类型不同,有的用1个字节记录,有的用2个字节记录。列的元数据解析列值至关重要。
如: MYSQL_TYPE_NEWDECIMAL(0xf6)有2个字节的元数据,第一个字节用于记录长度(precision), 第二个字节用于记录精度(scale):
decimal(8,2) meta_def = 0x0802
如: MySQL 5.6.4引入新的日期时间类型:TIME2, TIMESTAMP2, and DATETIME2, 在新类型中引入了"fractional seconds part",可以记录精度可以小于秒级,其中"fractional seconds part"的长度就记录在1个字节的元数据中:
timestamp(3) meta_def =0x03

8. NULL-bitmask

记录字段是否可以为空,通过位图方式记录,1个bit记录一个字段。

9. Option Mea Data

这是8版本后新增加的部分,根据MySQL的binlog_row_metadata的值,记录了列更多额外的信息。

具体代码实现可以参考: https://github.com/Li-Xiang/BinlogMiner/blob/master/src/org/littlestar/mysql/binlog/event/body/impl/TableMapEventBody.java

01-07 04:07
查看更多