count(*)与count(1)性能无差异,mysql 8.0+优化器将其等价处理,均跳过字段读取、只遍历索引计行;真正影响性能的是索引存在与否、where条件及引擎类型,而非括号内符号。
在现代 mysql(5.7+,尤其是 8.0)中,count(*) 和 count(1) 性能几乎完全一致,无需刻意替换;真正影响速度的是有没有索引、是否带 where 条件,而不是写 * 还是 1。
MySQL 8.0+ 的优化器把 COUNT(*) 和 COUNT(1) 当成一回事
从 MySQL 8.0 开始,优化器会将 COUNT(*) 直接等价转换为 COUNT(1) 处理——两者都跳过字段读取,只做行计数。源码级伪逻辑就是:
for each row in table: count++
不取任何字段值,也不判断 NULL。所以实测中多次运行,耗时差异通常在毫秒级甚至不可复现。
-
COUNT(*)不会“扫描所有列”,这是过时认知;它早已不是早期 MyISAM 那种全字段解析逻辑 -
COUNT(1)并非“更轻量”,它没有省掉任何底层操作,只是语义上更显式地表达“按行计” - Navicat 执行计划里看
EXPLAIN,两者的type、key、rows字段完全一样
COUNT(字段) 才是真慢,尤其字段允许为 NULL
只要用了具体字段名,比如 COUNT(email),InnoDB 就必须取出该字段值并判空——哪怕这个字段有索引,也比 COUNT(*) 多一次值读取和 NULL 判断。
- 字段定义为
NOT NULL(如主键id)时,COUNT(id)略快于COUNT(email),但仍慢于COUNT(*) - 字段允许为 NULL(如
email VARCHAR(100)),性能差距更明显,尤其数据量大时 - Navicat 的“实时行数”功能底层用的就是
COUNT(*),不是靠缓存或估算
别被老资料误导:主键索引 ≠ COUNT(主键) 更快
有人测试发现 COUNT(id) 比 COUNT(*) 慢一点,是因为 InnoDB 引擎层仍要从聚簇索引里逐行取出 id 值再交给 server 层累加;而 COUNT(*) 连这个取值动作都省了。
- 即使
id是主键,COUNT(id)仍需读取字段内容;COUNT(*)只遍历索引页头或叶子节点指针 - 如果表有覆盖索引(比如
INDEX idx_city (city)),且查询带WHERE city = 'Beijing',那么COUNT(*)会走该索引,但COUNT(email)可能被迫回表 - Navicat 中直接双击表名看到的“行数”,本质是
SHOW TABLE STATUS的Rows字段,它是 InnoDB 的估算值,不准;真要精确数,还是得跑COUNT(*)
真正该关注的不是 * 还是 1,而是你的查询有没有走索引、是否加了 WHERE、表引擎是不是 InnoDB。写 COUNT(*) 更符合 SQL 标准,语义清晰,且 MySQL 官方文档明确推荐——其他写法除了徒增代码歧义,没实际收益。











