结论:count(1)与count(*)在现代mysql innodb中性能几乎无差异,优化器会等价处理二者,均走轻量索引遍历路径;真正影响性能的是引擎类型、索引设计、where条件及是否需精确值。

直接说结论:在 MySQL 中,COUNT(1) 和 COUNT(*) 在绝大多数场景下性能几乎一致,所谓“用 COUNT(1) 替代 COUNT(*) 来提速”是一种过时的误解,尤其在现代 InnoDB 表和优化器下基本无效。
为什么 COUNT(1) 并不比 COUNT(*) 快
MySQL 优化器会对 COUNT(*) 自动做等价重写。只要没有 WHERE 条件、没涉及可空字段,优化器会识别出 COUNT(*) 实际只需统计行数,无需读取具体列值——这和 COUNT(1) 的执行逻辑完全相同。
- InnoDB 引擎内部对 COUNT(*) 和 COUNT(1) 都走“只遍历索引叶节点”的轻量路径(优先选最小的辅助索引,或主键索引)
- 两者都不取字段内容,也不判断 NULL,只是逐行计数
- 实测千万级表中,COUNT(*) 与 COUNT(1) 耗时差异通常在毫秒级,甚至完全一致
COUNT(1) 真正有优势的极少数情况
仅当满足以下全部条件时,COUNT(1) 才可能略快于 COUNT(*):
- 表使用 MyISAM 引擎(已极少用),且无任何索引 → COUNT(*) 仍需查磁盘元数据,而 COUNT(1) 可能触发不同路径(但差异微乎其微)
- 表无主键、无任何索引、且是 InnoDB → 此时优化器无法选择更小索引,必须聚簇索引全扫;COUNT(1) 理论上省去解析行结构的开销,但实际提升可忽略
- 你强制用了
FORCE INDEX指向大索引,而 COUNT(*) 被错误引导,COUNT(1) 却绕过了该限制(属异常行为,不应依赖)
真正影响性能的关键因素
比起纠结 COUNT(1) 还是 COUNT(*),这些才决定大表统计是否慢:
- 存储引擎:MyISAM 表的 COUNT(*) 是 O(1),InnoDB 则必须扫描(因 MVCC 导致行数不固定)
- 是否有可用的最小索引:例如有个仅含一个 TINYINT 字段的普通索引,COUNT(*) 会优先扫描它,远快于扫主键
- 是否加了 WHERE 条件:一旦带过滤,无论用哪个 COUNT 形式,都得走对应索引 + 回表或索引覆盖,性能取决于索引设计
-
是否需要精确值:如果允许误差,可用
SHOW TABLE STATUS中的Rows字段(但 InnoDB 下不准,偏差可达 40%+)
实用建议:大表快速获行数的替代方案
当 COUNT(*) 明显变慢(如秒级以上),不推荐换 COUNT(1),而应考虑:
- 业务层维护一张计数表,增删时用事务同步更新(适合写少读多、一致性要求高的场景)
- 用近似值:查询
information_schema.INNODB_SYS_TABLESTATS(需权限)或采样估算 - 加覆盖索引加速 COUNT(*),例如
CREATE INDEX idx_dummy ON t(a) WHERE a IS NOT NULL(配合常量条件) - 分库分表后,各子表 COUNT 后汇总,比单表扫更快











