2.4 重点Event之QUERY_EVENT和MAP_EVENT
2.4.1 QUERY_EVENT
1.QUERY_EVENT的作用
QUERY_EVENT不仅会记录一些语句的运行环境,比如SQL_MODE、客户端字符集、自增环境设置、当前登录数据库等,而且会记录执行时间。但对于行模式的 DDL 和 DML 记录的执行时间会有所不同,需要额外注意,如下。
DML:执行时间记录的是第一条数据更改后的时间,而不是DML语句执行的时间(一个DML语句可能修改很多条数据)。这个时间往往非常短,不能正确地表示DML语句执行的时间。语句部分记录的是begin。
DDL:执行时间记录的是实际语句的执行时间,语句部分记录的是实际的语句。
执行时间是 Seconds_Behind_Master 计算的一个影响因素,4.9 节将会详细介绍Seconds_Behind_Master的计算公式。一个事务只有一个QUERY_EVENT。
2.源码重要接口
主库
· 初始化构造函数:Query_log_event::Query_log_event(THD* ,char const* ,size_t,bool,bool,bool,int,bool);
· 写入binlog cache:Query_log_event::write(IO_CACHE* file)。
从库
· 读取构造函数:Query_log_event::Query_log_event(char const*,uint,binary_log::Format_description_event const* ,binary_log::Log_event_type);
· 应用函数:Query_log_event::do_apply_event。
3.主体格式
QUERY_EVENT包含固定和可变两部分,如图2-5所示。
图2-5
其中,固定部分如下。
slave_proxy_id:4字节,主库生成Event的thread id,它和show processlist中的id对应。
query_exec_time:4字节,这是执行时间。但是对于行模式的 DML 语句,这个执行时间并不准确,上面已经描述了原因。而对于DDL,它还是比较准确的。
db_len:1字节,用于描述数据库名的长度。
error_code:2字节,执行语句的错误码。
status_vars_len:2字节,status variables部分的长度。
可变部分如下。
status variables:环境参数,其中包含很多种格式。每种格式都有自己的长度和数据域,因此可以轻松地读取到各种值。比如SQL_MODE、客户端字符集、自增环境、客户端排序字符集等,但是其过于复杂,这里不做解析。
db:当前登录的database名字,以0x00结尾。主库来源为源码变量thd->db。如果是语句模式,则从库做过滤的时候会使用这个名字。
query:具体的语句。对于行模式的DML,记录的是begin,而对于DDL,记录的是具体的语句。
如果我们打开Query_log_event::do_apply_event函数,就会看到,这个Event在从库应用的时候会设置各种环境,比如客户端字符集、自增环境设置、当前登录数据库等,然后执行相应的语句,而对于行模式的DML,这里只会执行begin。注意一个细节,其中包含一段代码:
这段代码会设置线程的命令执行时间为Event header中Timestamp的时间,因此,我们在从库上执行now()函数时,是可以得到正确的结果的。
4.实例解析
下面是一个行模式的DML的QUERY_EVENT(mysqlbinlog--hexdump输出):
其中,固定部分如下。
06 00 00 00:thread id为6。
00 00 00 00:执行时间,对于行模式的DML来讲通常不准。
04:当前登录数据库名的长度。
00 00:错误码。
1a 00:status variables部分的长度,十六进制值1a就是十进制值26。
可变部分如下。
status variables:略。
74 65 73 74 00:当前登录库名test的ASCII编码,以0x00结尾。
42 45 47 49 4e:语句BEGIN的ASCII编码。
中间有一部分是status variables,这部分过于复杂,所以没有做实际解析。
5.生成时机
对于行模式的DML而言,生成时机是在事务的第一个DML语句的第一行数据修改之后。通常来讲,一个事务对应一个QUERY_EVENT。
DDL的生成时机在整个操作执行完成之后。