实现方式
-
MyISAM引擎把一个表的总行数存在了磁盘上,因此执行
count(*)
的时候会直接返回这个数,效率很高。(没有过滤条件的情况下) -
InnoDB执行
count(*)
的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。 -
InnoDB是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值。所以,普通索引树比主键索引树小很多。对于
count(*)
这样的操作,遍历哪个索引树得到的结果逻辑上都是一样的。因此,MySQL优化器会找到最小的那棵树来遍历。在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用法则之一。 -
count(*)
、count(主键id)和count(1) 都表示返回满足条件的结果集的总行数;而count(字段),则表示返回满足条件的数据行里面,参数“字段”不为NULL的总个数。 -
对于count(主键id)来说,InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层。server层拿到id后,判断是不可能为空的,就按行累加。
-
对于count(1)来说,InnoDB引擎遍历整张表,但不取值。server层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
-
对于count(字段)来说:
-
如果这个字段是定义为not null的话,一行行地从记录里面读出这个字段,判断不能为null,按行累加;
-
如果这个字段定义允许为null,那么执行的时候,判断到有可能是null,还要把值取出来再判断一下,不是null才累加。
-
-
但是
count(*)
是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count(*)
肯定不是null,按行累加。 -
按照效率排序的话,
count(字段)<count(主键id)<count(1)≈count(*)
,所以我建议尽量使用count(*)
。
大表统计问题
-
当表数据量大是,count(*)非常慢,可以使用单独的表来保存整个表行的计数以及相关条件的计数
1.先插入后更新 insert into 数据表 update 计数表,计数值加1。 并发系统性能的角度考虑,应该先插入操作记录,再更新计数表。因为更新计数表涉及到行锁的竞争,先插入再更新能最大程度地减少事务之间的锁等待,提升并发度。