深入理解MySQL主从原理
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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

img

我们可以观察到如下情况(输出做了适当换行)。

这是一个事务,其中,相同表的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是可变的。