结论:count(*)与count(1)在现代数据库中性能完全一致,均为统计总行数最优写法;count(列名)用于统计非null值,误用反而降低性能。

直接说结论:在绝大多数现代数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle)中,COUNT(*) 和 COUNT(1) 性能完全一致,且是统计总行数的最优写法;COUNT(列名) 不是用来“提升性能”的,而是用来统计非 NULL 值数量的——用错场景反而拖慢查询。
为什么 COUNT(*) 和 COUNT(1) 实际没区别?
现代查询优化器(如 MySQL 的 InnoDB 优化器、PostgreSQL 的 planner)会把 COUNT(*) 和 COUNT(1) 都识别为“仅需行存在性判断”,不会真正读取数据行内容。它们都会走最小可用索引(通常是主键索引),甚至在某些条件下跳过数据页扫描。
- MySQL 8.0.13+ 对无条件
SELECT COUNT(*) FROM tbl做了扫表路径优化,只遍历索引 B+ 树叶子节点 -
COUNT(1)并不比COUNT(*)“少解析一个 *”,两者 AST 解析后都归一为行计数指令 - 所谓“
COUNT(*)要展开字段列表所以更慢”是过时认知——优化器早就不这么干了
COUNT(列名) 什么时候快?什么时候慢?
COUNT(列名) 的行为本质是:逐行检查该列是否为 NULL,再计数。它的性能取决于列是否有索引、是否允许 NULL、以及优化器能否利用索引快速跳过 NULL 值。
- 如果该列是
NOT NULL且有索引(如主键),部分优化器可能等价优化为COUNT(*),但不保证(MySQL 就不自动做此推断) - 如果该列允许 NULL 且无索引,就必须全表扫描 + 每行判空,比
COUNT(*)多一次 NULL 检查开销 - 如果该列有二级索引且
NOT NULL,InnoDB 可能用该索引代替聚簇索引扫描(索引更小,I/O 更少)——这是唯一可能比COUNT(*)快的场景,但前提是明确需要“该列非 NULL 行数”,而非“总行数”
别踩这些坑:常见误用与反模式
很多团队试图靠换写法“优化 COUNT”,结果适得其反:
- 用
COUNT(id)替代COUNT(*),以为主键更快——实际 InnoDB 下两者执行计划完全相同,语义还变窄了(万一 id 允许 NULL?) - 在分页场景用
COUNT(*)查总页数,却没加覆盖索引或缓存——性能瓶颈根本不在 COUNT 写法,而在重复执行和缺乏预计算 - 在 WHERE 条件复杂时,盲目改用
COUNT(1)期待加速——真正影响性能的是 WHERE 的索引选择性,不是 COUNT 参数 - 用
COUNT(email)判断“用户总数”,而 email 允许 NULL——结果漏计,业务逻辑出错比慢更严重
真正影响 COUNT 性能的,从来不是括号里写 * 还是 1,而是:有没有合适的索引支撑 WHERE 条件、是否要实时精确值(可否用采样或缓存替代)、以及存储引擎本身的机制(比如 MyISAM 的行数缓存不可用于 InnoDB)。写对语义,比“调参式换写法”重要得多。










