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(*) 即使走最小索引,千万级也要几秒。常见优化:
- 近似值:用
EXPLAIN的 rows(不准确但快)。 - 汇总表:定时把 count 写到一张汇总表。
- 业务上不展示精确总数,只展示"下一页"按钮。
- 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)。优化器可能优化掉,但语义上没区别。