天天看点

MySQL · 引擎特性 · InnoDB COUNT(*) 优化(?)

在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,耗费的时间立马下降了….

让我们继续对新版本保持期待吧 :)

继续阅读