MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?
重点回答
count(*)、count(1)和count(字段名)都是用来统计行数的聚合函数。
InnoDB是通过B+tree来保存数据记录的,根据索引类型分为聚簇索引(主键索引)、非聚簇索引(二级索引),区别主要在于:聚簇索引的叶子节点存放的是实际数据,而二级索引叶子节点存放的是主键值,不是实际数据。
1)count(主键字段)
如果表中只有主键索引,没有二级索引,那InnoDB会循环遍历主键索引,将读取到的记录返回给server层,然后读取记录中的主键值,不为空,则将count变量+1;
但是,如果表中存在二级索引,那循环遍历对象就是二级索引,因为相同数量的二级索引记录比聚簇索引占用的空间更小,所以二级索引树比聚簇索引树小,这样一来,遍历二级索引的I/O成本就更低。因此,查询优化器会优先选择二级索引。
2)count(*)/count(1)
统计所有行,只计算行数,不做任何处理。
count(*)、count(1)效率一致,虽然网上有很多说法,不过官网还是进行了说明:
https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
InnoDB 处理 SELECT COUNT(*) 和 SELECT COUNT(1) 操作的方式相同。两者在性能上没有区别。
并且MySQL会对count(*)、count(1)进行优化,当有多个二级索引时,优化器优先选择key_len最小的索引进行扫描;没有二级索引,才会采用主键索引来统计。
3)count(字段名)
没有走索引,采用全表扫描的方式来计数,并且需要判断字段值是否为NULL,查询效率较低。
小结:
count(*)、count(1)和count(主键字段)在执行时,如果存在二级索引,查询优化器会选择二级索引(key_len最小的)进行扫描。
非必须不使用count(字段名)来统计行数,效率相对较低,会采用全表扫描的方式进行统计;如果有必须查询不为NULL的记录数需要,建议给该字段建立二级索引。
扩展
在MyISAM存储引擎中,由于只有表锁,因此会将每张表的总数进行单独记录维护(表锁使得对表的修改是串行,因此可以维护总数),所以count(*)非常快,相当于直接返回一个字段。前提是不需要条件过滤,而是直接返回整表数据。
相对比下,InnoDB支持行锁,会有很多并发来修改表的数据,所以无法维护记录总数,但是InnoDB也对count(*)、count(1)做了一定优化。当有多个二级索引时,优化器优先选择key_len最小的索引进行扫描;没有二级索引,才会采用主键索引来统计。前提也是没有对应的过滤条件。
评论