![深入理解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=1739545893-kFQIUFIQhhaNKQeuLIciuqSIOb8qXOm1-0-d29d31546079a93ccf5a023558a39b85)
图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=1739545893-Y5iLAg9jzomj5Aiv4wObyiKLxuNCrLhG-0-15363ea376950d5b0bd8d5c65cab5ef7)
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=1739545893-nMQMLYPYRc5ZyOQSUfQvhonDR2tmZa3C-0-b232135992327e53080fa01a5a499514)
如果感兴趣可以查看do_save_field_metadata函数。
m_null_bits:一个位图,用于表示字段是否可以为空。下面是位图的获取方式。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_26.jpg?sign=1739545893-2xX1yPyNuvjSJKwq1ejH60nW0GKGGFZa-0-ab9b070d5cf38b494df534db3f405e25)
4.实例解析
执行如下语句:
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_27.jpg?sign=1739545893-O529WDfte7vW7PPrFQ4wFeMwaY4V2G9I-0-10782b660ba420dd1c0387947db2ec22)
这个INSERT语句的MAP_EVENT如下:
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_28.jpg?sign=1739545893-IMQ9e0UWqjNHNKJqgbDapmLZnIV2n12y-0-d9a1bbf6f2fa8418fb98c8e1b58f1a22)
其中,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=1739545893-MzBszhExOGn1zCxC6o9hwFuhh8K6niBd-0-0ec6f4e0c07b3384777b20af7311df6b)
我们可以观察到如下情况(输出做了适当换行)。
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_30.jpg?sign=1739545893-ZP1ggme9XCbMPH4KsM1QNP5oRjM3CDhg-0-f5fa11c98b4bb8de6da32277613cd999)
![](https://epubservercos.yuewen.com/A01218/19823444008569806/epubprivate/OEBPS/Images/txt002_31.jpg?sign=1739545893-dKeGZLWFnpr83tBigCjKjK1FRA2rIlK0-0-e07415664dfb44dda1cd443a3c012460)
这是一个事务,其中,相同表的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是可变的。