下面是官方文档的描述,我理解可能有问题,但是官方文档肯定靠谱的
COUNT(*)
is somewhat different in that it returns a count of the number of rows retrieved, whether or not they containNULL
values.For transactional storage engines such as
InnoDB
, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.
InnoDB
does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently,SELECT COUNT(*)
statements only count rows visible to the current transaction.To process a
SELECT COUNT(*)
statement,InnoDB
scans an index of the table, which takes some time if the index is not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, useSHOW TABLE STATUS
.
InnoDB
handlesSELECT COUNT(*)
andSELECT COUNT(1)
operations in the same way. There is no performance difference.
注意本文的讨论背景是InnoDB引擎。
count 本身是一个聚合函数,本质过程无非是对选择的结果集进行一行行的判断,如果count参数不是NULL,累计值就加1,否则(count的参数 is NULL)就不加了,最后返回总累计值。
先说结果
按照效率排序的话 count(*)=count(1)>count(主键Id)>count(字段)
所以当然是尽量使用 count(*) 啦!
分析原因之前,先简单分析一下 MySQL 的 server层和引擎层的两个原则:
server 要什么引擎层就会提供什么;
引擎层只会提供必要的值
另外,要注意的是优化器对 count(*) 进行了专门优化
下面分析一下原因:
count(id)
引擎层会遍历整张表,把每一行的id都取出来,返回给server层,server 层会根据id判断,不为NULL的就会累计值加一。
count(1)
InnoDB引擎也会遍历整张表,但是不用取值。server层会对每一个返回的1进行判断,如果不为NULL,累计值加一。
count(字段)
如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不
能为 null,按行累加;
如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值
取出来再判断一下,不是 null 才累加。
count(*)
这里InnoDB会先扫描一个(最优的)索引,如果索引不全部在 buffer pool 会需要点时间加载。
官方文档说对于更快的统计,可以创建一个技术的表,只要涉及add 和 delete 可以进行更新。当然在高并发场景这样可能不太好用。
近似统计 SHOW TABLE STATUS
如果不准确的统计可以接受,可以使用 SHOW TABLE STATUS
进行近似的统计。