2.5 重点Event之WRITE_EVENT和DELETE_EVENT
2.5.1 WRITE_EVENT
1.WRITE_EVENT的作用
本Event是INSERT语句生成的Event,主要用于记录INSERT语句的after_image实际数据,其中还包含 table_id、映像位图、字段数量、行数据位图等信息。实际上所有的 DML 语句虽然从客户端看都是一条语句,但是Event记录的时候都是以行为单位的,而且是更改一行记录一行,3.2节将详细说明这个流程。
2.源码重要接口
本Event和后面的UPDATE_EVENT和DELETE_EVENT都来自同一个父类Rows_log_event。
主库
· 初始化构造函数:Write_rows_log_event::Write_rows_log_event(THD* ,TABLE* ,const Table_id& table_id,bool is_transactional,const uchar* extra_row_info)。
· 数据写入函数:Rows_log_event::do_add_row_data。
· 写入binlog cache函数:Rows_log_event::write_data_header,Rows_log_event::write_data_body。
从库
· 读取构造函数:Write_rows_log_event(const char *buf,uint event_len,const Format_description_event *description_event)。
· 应用函数:Rows_log_event::do_apply_event。
3.主体格式
本Event的主体格式如图2-7所示。
图2-7
其中,固定部分如下。
table_id:6 字节,我们看到本 Event 中并没有包含访问表的具体信息,只包含了一个table_id。因为我们的MAP_EVENT中已经包含了相应的table_id到实际访问表的映射,这里记录table_id就可以了。
Reserved:2字节,保留以后使用。
可变部分如下。
var_header_len:当前2字节,通常为0x02 00。可以参考Rows_log_event::write_data_header函数。
columns_width:字段个数。
columns_after_image:after_image为位图,最少1字节。这里主要表示Event中是否需要记录全部的字段值,受到参数binlog_row_image影响,后面会单独讨论。这里需记住,如果参数binlog_row_image设置为FULL,那么columns_after_image会记录为0xff,如下。
row Bit-field:位图,最少1字节。这个数据是行数据自带的,也就是构造Event的时候传入的,每位代表一个字段。如果有实际数据则为0,否则为1。注意在MAP_EVENT中,有一个表示字段属性是否可以为NULL的位图m_null_bits,这里的位图是某个字段数据是否实际为NULL,两者并不一样。
row real data:实际行数据。这个数据是行数据自带的,按照字段的顺序排列。
4.实例解析
我们进行如下操作。
使用解析语句如下。
结果如下。
解析如下。
7e 00 00 00 00 00:table_id,十六进制值7e,即十进制值126。
01 00:保留。
02 00:固定为0x02 00。
03:字段个数。
ff:columns_after_image,如果参数binlog_row_image设置为FULL,则固定为0xff。
f8:位图,转换为二进制值11111000,代表3列都有实际数据。
01 00 00 00:实际数据为1。
07 67 61 6f 70 65 6e 67:0x07表示可变长度类型varchar的长度为7字节,67 61 6f 70 65 6e 67为gaopeng字符串的ASCII编码。
03 00 00 00:实际数据为3。
5.生成时机
本Event只会在行模式下生成,后面我们马上要讲的UPDATE_EVENT/DELETE_EVENT和它一样,通常它们生成的时机都是在第一条数据在InnoDB层变更完成,并且在QUERY_EVENT和MAP_EVENT之后。
6.修改多行数据
如果需要变更的数据不止一行,那么怎么处理呢?DML 语句可以使用一条语句变更多条数据,在这种情况下,只需要将行数据增加到这个 Event 就可以了,参考Rows_log_event::do_add_row_data函数。
一个Event可以无限大吗?显然是不行的,在 THD::binlog_prepare_pending_rows_event函数中有如下判断。
我们可以清晰地看到,如果本次加入的数据(needed)加上已有的数据的大小大于opt_binlog_rows_event_max_size的设置,那么就会建立一个新的Event来继续存储。opt_binlog_rows_event_max_size的实际大小是8192字节,如下。
因此,我们可以认为一个DML Event中可以包含多行数据,但是其大小应该在8192字节左右,大事务可能包含多个DML Event。
7.写入binlog cache的时机
如上所述,如果DML语句涉及多行数据的变更,根据修改量的大小可能生成多个DML Event。那么它们在什么时候写入binlog cache呢?实际上,MySQL每次新开启一个DML Event之后都会将现有的Event写入binlog cache,下面是THD::binlog_prepare_pending_rows_event函数的片段。
因此,并不需要等到所有的DML Event都生成后才一次性写入binlog cache,那样会带来更多的内存消耗,是不可取的。
后面的UPDATE_EVENT和DELETE_EVENT的生成时机和写入binglog cache的时机都和这里的WRITE_EVENT一致,就不再详细描述了。