注册 登录
  • 欢迎访问"运维那点事",推荐使用Google浏览器访问,可以扫码关注本站的"微信公众号"。
  • 如果您觉得本站对你有帮助,那么可以扫码捐助以帮助本站更好地发展。

MySQL 5.7 InnoDB COUNT(*)优化

MySQL 5.7 彭东稳 7093次浏览 已收录 0个评论

在MySQL 5.7版本中,InnoDB实现了新的handler的records接口函数,当你需要表上的精确记录个数时,会直接调用该函数进行计算。

使用

实际上records接口函数是在优化阶段调用的,在满足一定条件时,直接去计算行级计数。其explain出来的结果相比老版本也有所不同,这里我们使用sysbench的sbtest表来进行测试,共200万行数据。

注意这里Extra里为”Select tables optimized away”,表示在优化器阶段已经被优化掉了。如果给id列带上条件的话,则回退到之前的逻辑。

实现

WL#6742中,为InnoDB实现了handler的records函数接口

函数栈

  • HA_HAS_RECORDS:引擎flag,表示是否可以把count(*)下推到引擎层
  • 总是使用聚集索引来进行计算行数
  • 只需要读取主键值,无需去读取外部存储列(row_prebuilt_t::read_just_key),如果行记录较大的话,就可以节省客观的诸如内存拷贝之类的操作开销
  • 计算过程可中断,每检索1000条记录,检查事务是否被中断
  • 由于只有一次引擎层的调用,减少了Server层和InnoDB的交互,避免了无谓的内存操作或格式转换
  • 对于分区表,在5.7版本已经下推到innodb层,因此分区表的计算方式(ha_innopart::records)是针对每个分区调用ha_innobase::records,再将结果累加起来

缺点

由于总是强制使用聚集索引,缺点很明显:当二级索引的大小远小于聚集索引,且数据不在内存中时,使用二级索引显然要快些,因此文件IO更少。如下例:

默认情况下检索所有行(以下测试都是在清空buffer pool时进行的):

即时强制指定索引也没用 MySQL 5.7 InnoDB COUNT(*)优化

但如果带上一个简单的条件,让select count(*)走索引k_1,耗费的时间立马下降了….

个人认为这算是一个性能退化,退一步讲,如果用户知道force index能够走一个更好的索引来计算行数,优化器应该做出选择,而不是总是无条件选择聚集索引,提了个Bug到官方,在MySQL 5.7.18已经还原为原来的版本,原话如下:

由于MySQL 5.7.2对Count(*)中引入了修改,导致在某些情况下,Innodb通过遍历聚集索引而不是较小的辅助索引来计算行数,因此性能有所倒退。在MySQL 5.7.18中修改被还原。

其他

WL#6742还提到了一个尚未公布的WL#6605,从其只言片语中可以推断官方有意向实现即时获得行数:

The next worklog, WL#6605, is intended to return the COUNT(*) through this handler::records() interface almost immediately in all conditions just by keeping track if the base committed count along with transaction deltas.

让我们继续对新版本保持期待吧 MySQL 5.7 InnoDB COUNT(*)优化

<转载>

http://mysql.taobao.org/monthly/2016/06/10/

https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-18.html


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (6)or分享 (0)
关于作者:

您必须 登录 才能发表评论!