最近在一个项目中DBA同学问了一个问题:为什么很多慢查询日志中显示 Rows_examined : 0?
需要说明的是, 这类慢查询语句都是类似 select count(*) from (…)t;
在说明这个问题之前,我们先指出两个相关背景:
1、MySQL的临时表,都是MyISAM的。
2、MyISAM表中的记录总数是额外存储的,count(*)的时候不需要遍历数据。
3、把count(*)转换为取一个const值这件事情,是在优化(optimize)阶段作的。
问题分析:
这个值对应于代码中的examined_row_count,用于统计每次执行过程中实际扫描的记录数。
正常的流程:
查询执行过程中,每个子查询的信息都在curr_join,其中curr_join->examined_rows在每次扫一行的时候++.子查询完成后,curr_join->examined_rows累积到examined_row_count中。
哪里清0的?
我们上面这个语句,from内的子查询,curr_join->examined_rows是正常的,但在外部计算count的时候,上面提到的优化结果认为这个阶段是不需要扫描表的,把thd->examined_row_count给置0了。罪魁代码在JOIN::exec()中。
从代码中的注释来看,似乎是一个没有考虑细致的地方,待求证。
改进分析:
纵然有很多理由,在慢查询日志中显示的0还是不友好的,可以理解为是一个bug。
实际上从上面的分析可知,如果是复合查询中的一个环节,尤其不是第一个环节,此处清0会使显示结果出错。从当前的thd信息中可以判断出是否使用了子查询,简单一点的修改,根据thd.derived_tables信息来确定是否清0。
实际上每次执行开始之前的这个值是被reset过的,有理由怀疑这个地方实际上可以直接删除这句话。这个比较激进,要求证一下。
简单验证:
加了thd.derived_tables判断后,
方便调试起见,把所有的查询都打到slow_log了。