![深入理解MySQL主从原理](https://wfqqreader-1252317822.image.myqcloud.com/cover/513/37423513/b_37423513.jpg)
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所示。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_23.jpg?sign=1739287510-O1xS0RwEBO3i5WXOUqzVFIH6wxsWlUXg-0-07511a4a48bc4df612edb847514a11ce)
图2-6
其中,固定部分如下。
table_id:6字节,这个table_id和InnoDB层的table_id不一样,它分配的时机是第一次打开表定义时。它不是固定的,重启MySQL实例或者执行flush table命令都会导致其改变。下面是table_id更改的代码:
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_24.jpg?sign=1739287510-RNV4qqTpMDi7iBkXuE7cbPZwdK7iqX5h-0-48136e99a32a6f15c94208e05d4888f5)
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字节。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_25.jpg?sign=1739287510-jOLywpn86oFQMiElArqUPSdlsTV6fkkt-0-11a56ccd1f5efc46c20cddd539b22afc)
如果感兴趣可以查看do_save_field_metadata函数。
m_null_bits:一个位图,用于表示字段是否可以为空。下面是位图的获取方式。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_26.jpg?sign=1739287510-gxgzmxnZ2YGxcAiRcOQFHV9fbBkEKa6Q-0-214bc6c9b0926ef895455b301c0dcc84)
4.实例解析
执行如下语句:
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_27.jpg?sign=1739287510-el5cUADSq7v5o5tZVOkuuJRMLl5UKDX6-0-66865c91f17b9cc4701646a8ed3bb36d)
这个INSERT语句的MAP_EVENT如下:
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_28.jpg?sign=1739287510-4klbj8bOJ8sUfZ3uzEHnfxSwuxctA5Z9-0-648581efb69f35885437823ee8e7bd8b)
其中,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](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_29.jpg?sign=1739287510-1v37YP8hTWW05aRBwjOz3GadyHCVUuLO-0-3e0d0b022cae3df6c923e93e0b615dea)
我们可以观察到如下情况(输出做了适当换行)。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_30.jpg?sign=1739287510-4QQTCIi8GYTMxnRD2ncbifWyzfAqGnTA-0-fc878f2b636650fe31ad33a57ec7c3c8)
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_31.jpg?sign=1739287510-1RoGxjDxp7XQGVdygFnLOiHA3G6ZZVD8-0-736be34801c52ab98e8ea8cdf6837b9c)
这是一个事务,其中,相同表的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是可变的。