2.4.2 MAP_EVENT
1.MAP_EVENT的作用
MAP_EVENT是行模式特有的,它的主要作用是映射table id和实际访问表。其中还包含了一些表的定义,如表所在库名、表名、字段类型、可变字段长度等。这里的库名和QUERY_EVENT的库名不一样,这个库名来自表的定义,而QUERY_EVENT的库名来自当前登录的数据库,即源码变量thd->db。
2.源码重要接口
主库
· 初始化构造函数:Table_map_log_event::Table_map_log_event(THD *thd_arg,TABLE *tbl,const Table_id& tid,bool is_transactional);
· 写入binlog cache:Table_map_log_event::write_data_header,Table_map_log_event::write_data_body。
从库
· 读取构造函数:Table_map_log_event(const char *buf,uint event_len,const Format_description_event *description_event);
· 应用函数:Table_map_log_event::do_apply_event。
3.主体格式
MAP_EVENT的主体格式如图2-6所示。
图2-6
其中,固定部分如下。
table_id:6字节,这个table_id和InnoDB层的table_id不一样,它分配的时机是第一次打开表定义时。它不是固定的,重启MySQL实例或者执行flush table命令都会导致其改变。下面是table_id更改的代码:
Reserved:2字节,保留以后使用。
可变部分如下。
db len:表所在数据库名的长度。
db name:实际数据库名,以0x00结尾。
table len:表名的长度。
table name:实际表名,以0x00结尾。
no of cols:表中字段数量。
array of col types:字段的类型数组。
metadata len:metadata block的长度。
metadata block:对于可变字段需要记录字段的长度,但对于int这种数据类型就不需要了,因为它的长度是固定的。下面代码是varchar关于可变长度的输出,它占用2字节。
如果感兴趣可以查看do_save_field_metadata函数。
m_null_bits:一个位图,用于表示字段是否可以为空。下面是位图的获取方式。
4.实例解析
执行如下语句:
这个INSERT语句的MAP_EVENT如下:
其中,6c 00 00 00 00 00:表示table_id,即十六进制值6c,转换为十进制值108。
01 00:保留。
02:表所在的数据库名长度为2。
67 70 00:数据库名gp的ASCII表示,以0x00结尾。
02:表名的长度为2。
74 79 00:表名ty的ASCII表示,以0x00结尾。
03:表拥有3个字段。
03 03 03:每个字段的类型都是03,实际就是int。具体可以参考enum_field_types这个枚举类型。
00:metadata长度为0,没有可变字段。
06:位图,即二进制值110,表示第一个字段不可以为空,其他两个字段可以为空。
5.生成时机
本 Event 只会在行模式下生成。生成时机是事务的每条 DML 语句修改的第一行数据在InnoDB 引擎层修改完成,并且在QUERY_EVENT生成之后。通常来讲,每个语句的每个表都会包含这样一个MAP_EVENT。
6.table_id的易变性
前面我们说过了,table_id是可变的,现在来构造这种情况,如表2-1所示,还是使用上面的ty表。
表2-1
我们可以观察到如下情况(输出做了适当换行)。
这是一个事务,其中,相同表的table_id却不一样,可以观察到如下现象。
· at 2434:Table_map:gp.ty mapped to number 133。
· at 2527:Table_map:gp.ty mapped to number 147。
我们发现,这里同样的ty表对应了两个不同的table_id,证明table_id是可变的。