count(*)的实现方式
不同的存储引擎在实现count(*)的方式上不同
MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回
这个数,效率很高;
而 InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面
读出来,然后累积计数。
如果加入了where条件,MyISAM也不可能返回那么快。
show table status
使用命令,会返回TABLE_ROWS,但是这个数是基于采样计数的不够准确。
区别引擎和show table status
MyISAM 表虽然 count(*) 很快,但是不支持事务;
show table status 命令虽然返回很快,但是不准确;
InnoDB 表直接 count(*) 会遍历全表,虽然结果准确,但会导致性能问题。
如果用缓存系统存储计数
存在丢失更新和数据不同步的情况,因为缓存和数据库同时修改并非原子操作。
把计数放在 Redis 里面,不能够保证计数和 MySQL 表里的数据精确一致的原因,
是这两个不同的存储构成的系统,不支持分布式事务,无法拿到精确一致的视图。而把计数值也放在 MySQL 中,就解决了一致性视图的问题。
数据库保存计数
- 不会丢失更新
- 同时利用事务特性,同步计数和实际表内容
不同count用法
count语义,聚合函数,对于返回的结果集合一行行地进行判断,如果count的值不为空,计数加一,最后返回累计值。
count(*)、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数;而
count(字段),则表示返回满足条件的数据行里面,参数“字段”不为 NULL 的总个数。
- count(*),优化器优化了 count(*) 的语义为“取行数”
- count(主键 id),InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返
回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。 - count(字段)
- 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;
- 如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加
- count(1) InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一
行,放一个数字“1”进去,判断是不可能为空的,按行累加。
结论
按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*),所
以我建议你,尽量使用 count(*)。