MySQL 统计总行数:听说 count(*) 性能更好,是真的吗
在日常的开发工作中,统计一张表的总行数是一个很常见的需求。
通常我们会使用这样一条 sql 查询数据:select count(*) from table t。但是,随着表中的数据越来越多,这条 sql 的执行速度也会越来越慢。
今天我们就来聊一聊,count(*)是如何统计一张表中的总行数的,以及 count(*)、count(1)、count(id)、count(字段)这几种方式的性能差异。
1,count(*)的执行方式
首先,根据官方的描述,使用不同的执行引擎,count(*)的实现方式是有区别的。
在 MyISAM 引擎中,表的总行数是直接存在磁盘上,所以查询的时候可以直接返回,效率很高。
但是对于 InnoDB 引擎来说,情况就不一样了。由于多版本并发控制 MVCC 的存在,InnoDB 在执行 count(*)时,无法确定表中到底有多少行,只好把数据一行一行的从引擎中读出来,然后累加计数。
那 InnoDB 为什么要这么麻烦呢?为什么不像 MyISAM 那样,把总行数存在磁盘上呢?
这是因为在同一时刻,可能存在多个事务查询,因为 MVCC 的原因,不同事务是相互隔离的,InnoDB 需要在当前事务中,一行一行的读取数据,然后判断哪些数据对自己是可见的,哪些数据对自己是不可见的。
所以随着表中数据越来越多,我们会发现 count(*)的速度也会越来越慢了。
2,不同 count 用法的差异
首先我们要清楚,count 是一个聚合函数,对查到的结果集,会一行一行的判断,如果不为 NULL,累计值就加 1,最终得到一个值。
1,对于 count(主键 id)来说,InnoDB 引擎遍历表中的每一行,将主键 id 获取到以后返回给 Server 层,Server 层判断 id 不为 null,然后进行累加。(ps.主键 id 肯定是不为空的,正常来说是不需要判断的,但是官方源码中确实有这个判断)
2,对于 count(1)来说,InnoDB 依然遍历表中的每一行,但是并不获取值(返回一行,不取字段),Server 对于返回的数据,直接放一个“1”进去,然后判断不为 null,进行累加。
3,对于 count(字段)来说,遍历表中的每一行,读出这个字段,Server 层判断是否为 null,如果不为 null,则进行累加,如果为 null,就忽略这一行。
4,对于 count(*)来说,MySQL 专门做了优化,就像 count(1)那样,并不取值,count(*)肯定不是 null,直接按行累加。
由于 count(*)并不需要对获取的值进行 null 判断,所以性能理论上会比 count(1)更好一点。但是一般认为这点影响微乎其微,并没有什么实际性差异。
这一点在 MySQL 官方文档上也有说明:
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference。
3,总结
按照执行效率来说:count(字段)<count(主键 id)<count(1)≈count(*)。
一般情况下,count(*)是更好的选择。
finally,朋友们点赞关注支持一下吧。
版权声明: 本文为 InfoQ 作者【程序员拾山】的原创文章。
原文链接:【http://xie.infoq.cn/article/f0f8185113df16662c816188a】。未经作者许可,禁止转载。
评论