MySQL 中 COUNT(*)、COUNT(1)、COUNT(字段) 有什么区别?
在 MySQL 中统计数据表的行数可以使用三种方式,分别是 SELECT COUNT(*)
、SELECT COUNT(1)
、SELECT COUNT(字段)
,那么三者之间有什么区别呢?
COUNT(*)
和 COUNT(1)
都是对所有结果进行 COUNT
,COUNT(*)
和 COUNT(1)
本质上并没有区别(二者执行时间可能略有差别,但是它们的执行效率可以认为是相等的)。在查询语句中,如果存在 WHERE
子句,则是对所有符合筛选条件的数据行数进行统计;如果没有 WHERE
子句,则是对数据表的数据行数进行统计。
COUNT(*)
和 COUNT(1)
虽然执行效率相等,但在 MySQL 不同存储引擎下的时间复杂度也存在区别:
-
对于 MyISAM 存储引擎:
如果是在 MyISAM 存储引擎下执行
COUNT(*)
和COUNT(1)
,那么统计数据表的行数只需要O(1)
的时间复杂度,这是因为每张 MyISAM 的数据表都有一个 meta 信息存储了row_count
值,而一致性则由表级锁来保证。 -
对于 InnoDB 存储引擎:
如果是 InnoDB 存储引擎,因为 InnoDB 存储引擎支持事务且采用行级锁和 MVCC 机制的特性,所以 InnoDB 存储引擎无法像 MyISAM 存储引擎一样维护一个
row_count
变量,因此 InnoDB 执行存储引擎执行COUNT(*)
和COUNT(1)
需要采用扫描全表,对应的时间复杂度为O(n)
,以循环加计数的方式来完成数据行数统计。
对于 COUNT(字段)
而言,在 InnoDB 存储引擎中,如果采用 COUNT(字段)
来统计数据行数,要尽量采用二级索引。这是因为聚簇索引中单个数据节点包含的数据信息要多于二级索引单个数据节点所包含的数据信息,因此如果加载相同个数的数据节点,二级索引(非聚簇索引)所占内存要比聚簇索引更少。
而对于 COUNT(*)
和 COUNT(1)
而言,它们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。如果有多个二级索引,会使用 key_len
小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引来进行统计。