跳转至

MySQL count(1) 和 count(*) 性能对比?

结论

InnoDB 下,count(*)count(1) 性能基本一致;count(字段) 会略慢,因为要判断 NULL。 不要被"count(1) 更快"的老谣言误导。

一、MySQL 不同版本的实现

MyISAM

count(*) 不带 WHERE 时,MyISAM 直接读表元信息里保存的行数,O(1)。带 WHERE 仍然要扫表。

InnoDB

InnoDB 没有类似 MyISAM 的精确行数元信息(因为 MVCC 下每个事务看到的行数不同),所以:

  • count(*):InnoDB 会选最小的索引遍历,逐行计数。
  • count(1):同样遍历索引,不取值。

所以两者性能相当。

二、三者的区别

写法 含义 性能
count(*) 统计行数,不取值
count(1) 同上,每行当 1 计数 与 count(*) 相当
count(字段) 统计该字段非 NULL 的行数 略慢,要判断 NULL
count(distinct 字段) 去重计数 更慢

三、为什么 InnoDB 选最小索引

InnoDB 是索引组织表(IOT):

  • 主键索引(聚簇索引)包含完整行数据。
  • 二级索引只包含主键值 + 索引列。

遍历二级索引比遍历聚簇索引体积小,所以优化器选最小的二级索引来 count。

四、为什么不能用 count(*) 做分页总条数

大表 count(*) 即使走最小索引,千万级也要几秒。常见优化:

  1. 近似值:用 EXPLAIN 的 rows(不准确但快)。
  2. 汇总表:定时把 count 写到一张汇总表。
  3. 业务上不展示精确总数,只展示"下一页"按钮。
  4. Redis 维护计数:插入/删除时异步更新。

五、为什么老说 count(1) 比 count(*) 快

这是 MyISAM 时代的误解。InnoDB 5.6 以后,优化器已经能识别 count(*) 是计数,不取具体列。

MySQL 官方说明

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

—— MySQL Reference Manual

count(主键) 呢?

count(id)count(*) 略慢,因为它要取出 id 值判断是否 NULL(虽然主键 NOT NULL)。优化器可能优化掉,但语义上没区别。