count(*) 的实现方式:
1. MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回
这个数,效率很高;
2. 而 InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面
读出来,然后累积计数。
这里需要注意的是,我们在这篇文章里讨论的是没有过滤条件的 count(*),如果加了
where 条件的话,MyISAM 表也是不能返回得这么快的。
为什么 InnoDB 不跟 MyISAM 一样,也把数字存起来呢?
这是因为即使是在同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,
InnoDB 表“应该返回多少行”也是不确定的。
在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则
之一。
解决计数的方法:
1. 用缓存系统保存计数
存在的问题:1)计数丢失;2)计数不准确
2. 在数据库保存计数 ----> 常用的手段
不同的 count 用法
1). 对于 count(主键 id) 来说
InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。
2). 对于 count(1) 来说
InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
count(1) 执行得要比 count(主键 id) 快。因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。
3). 对于 count(字段) 来说:
1. 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不
能为 null,按行累加;
2. 如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值
取出来再判断一下,不是 null 才累加。
4). count(*)
并不会把全部字段取出来,而是专门做了优化,不取值。count(*) 肯定不是 null,按行累加。
所以结论是:按照效率排序的话
count(字段)<count(主键 id)<count(1))≈count(*),尽量使用 count(*)。