在5.7版本中,innodb实现了新的handler的records接口函数,当你需要表上的精确记录个数时,会直接调用该函数进行计算。
实际上records接口函数是在优化阶段调用的,在满足一定条件时,直接去计算行级计数。其explain出来的结果相比老版本也有所不同,这里我们使用sysbench的sbtest表来进行测试,共200万行数据。
注意这里extra里为”select tables optimized away”,表示在优化器阶段已经被优化掉了。如果给id列带上条件的话,则回退到之前的逻辑
函数栈
ha_has_records:引擎flag,表示是否可以把count(*)下推到引擎层
总是使用聚集索引来进行计算行数
只需要读取主键值,无需去读取外部存储列(row_prebuilt_t::read_just_key),如果行记录较大的话,就可以节省客观的诸如内存拷贝之类的操作开销
计算过程可中断,每检索1000条记录,检查事务是否被中断
由于只有一次引擎层的调用,减少了server层和innodb的交互,避免了无谓的内存操作或格式转换
对于分区表,在5.7版本已经下推到innodb层,因此分区表的计算方式(ha_innopart::records)是针对每个分区调用ha_innobase::records,再将结果累加起来
相关代码:
<a href="https://github.com/mysql/mysql-server/commit/510dd48bf510dc0a3bda9e62cede698325d05fdd" target="_blank">commit1</a>
<a href="https://github.com/mysql/mysql-server/commit/40ec5373c044547a66d5456b15d61553de8f3401" target="_blank">commit2</a>
由于总是强制使用聚集索引,缺点很明显:当二级索引的大小远小于聚集索引,且数据不在内存中时,使用二级索引显然要快些,因此文件io更少。如下例:
默认情况下检索所有行(以下测试都是在清空buffer pool时进行的):
即时强制指定索引也没用 :(
但如果带上一个简单的条件,让select count(*)走索引k_1,耗费的时间立马下降了….
让我们继续对新版本保持期待吧 :)