在MySQL中,不同的存储引擎,count(*)有不同的实现方式。
MyISAM引擎把一个表的总行数存在在磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高
而InnoDB引擎就很麻烦了,它执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数
以上都是没有加过滤条件的count(*),如果加了where条件的话MyISAM表也是不能返回这么快的。
我们也知道show table status命令的话,就会发现这个命令的输出结果里面也有一个TABLE_ROWS用于显示这个表当前有多少行,这个命令执行挺快的,那这个TABLE_ROWS能代替count(*)吗?
答:当然不可以,TABLE_ROWS是通过采样估算得来的,因此不是很准确,官方文档提到误差可能达到40%~50%,所以show table status 命令显示的行数也不能直接使用
小结:MyISAM表虽然count(*)很快,但是不支持事务
show table status命令虽然返回很快,但是不准确
InnoDB表直接count(*)会遍历全表,虽然结果准确,但是会导致性能问题
如果你现在有一个页面经常要显示交易系统的操作记录总数到底应该怎么办呢?
答案是我们只能自己计数。
- 用缓存系统保存计数
- 在数据库保存计数
说一下count(*)、count(主键id)、count(字段)、count(1)性能有哪些差别?
- count(主键id),InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层,server层拿到id之后,判断是不可能为空的,就按行累加。
- count(1),InnoDB引擎遍历整张表,但不取值,server层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
单看这两个的话count(1)要比count(主键id)要快,因为从引擎返回id会涉及到解析数据行,以及拷贝字段值的操作。
- count(字段)
- 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;
- 如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。
也就是前面的第一条原则,server层要什么字段,InnoDB就返回什么字段
- count(*)、并不会把全部字段取出来,而是专门做了优化,不取值。count(*)肯定不是null,按行累加
结论:按照效率排序的话,count(字段) < count(主键id) < count(1) 约等于count(*),所以尽量使用count(*)