首页 > 数据库 >MySQL中COUNT(id)和COUNT(*)哪个效率更高?

MySQL中COUNT(id)和COUNT(*)哪个效率更高?

来源:互联网 2026-07-27 08:42:02

InnoDB引擎下,COUNT(*)与COUNT(id)性能持平,前者略优;网传COUNT(id)更快是谣言。统计总行数应统一用COUNT(*),禁止用COUNT(普通字段),避免逻辑错误与性能损耗。

前言

在开发中统计行数,很多人习惯使用两种写法:

MySQL中COUNT(id)和COUNT(*)哪个效率更高?

长期稳定更新的攒劲资源: >>>点此立即查看<<<

SELECT COUNT(*) FROM `user_login_log` WHERE `date` = CURDATE();SELECT COUNT(id) FROM `user_login_log` WHERE `date` = CURDATE();

网上曾流传一种说法:COUNT(*) 需要扫描整行数据,而 COUNT(id) 只读取主键,因此 COUNT(id) 更快。

这个观点在当前的 InnoDB 引擎下,完全不成立

本文基于 InnoDB 底层原理,详细解释 COUNT(*)COUNT(id)COUNT(普通字段) 的差异,并给出生产环境的标准编码规范。

先说明环境:全文基于 MySQL InnoDB(5.7 / 8.0,线上最常用版本);MyISAM 机制不同,文末单独提及。

一、先搞懂三个COUNT语法的语义

1. COUNT(*)

根据 SQL 标准定义:统计满足查询条件的所有行数,不进行任何 NULL 判断。

MySQL 官方对 COUNT(*) 做了专门优化,优化器会优先选择当前表体积最小的二级索引进行扫描计数,无需读取聚簇索引的完整行数据。

2. COUNT(id)

语义:统计 id IS NOT NULL 的记录行数。

通常 id 是主键,主键强制非空,因此逻辑上等价于统计总行数。

执行逻辑:扫描索引,取出主键 id 值,判断不为 NULL 后再计数。

3. COUNT(普通业务字段)

COUNT(login_ip)

语义:统计 login_ip IS NOT NULL 的记录。

这里存在两个风险:

  1. 如果字段允许 NULL,统计结果与实际行数不一致,易引发业务 BUG;
  2. InnoDB 需要取出字段真实值来判断 NULL,开销比 COUNT(*) 大很多。

二、核心结论(InnoDB)

带 WHERE 条件统计时,COUNT(*) 和 COUNT(id) 的性能几乎没有差异。

无需纠结选哪个,两者的执行计划、扫描行数、IO 开销基本持平。

底层原因很简单:InnoDB 二级索引的叶子节点本身就存放着主键 id。无论优化器选择哪个二级索引进行扫描计数:

  • COUNT(*):只需计数索引条目,不需要读取字段值
  • COUNT(id):除了计数,还要额外取出 id 值做非空判断

理论上 COUNT(*)略微优于 COUNT(id),但在绝大多数场景下,这种差异几乎无法感知。

三、误区拆解:为什么会流传 COUNT(id) 更快?

这个谣言的来源,主要来自老旧 MyISAM 的认知混淆,加上早期网络文章以讹传讹:

  1. MyISAM 在不带 WHERE 条件时,COUNT(*) 确实很快,因为引擎缓存了总行数;但 MyISAM 现在已不是主流;
  2. 很多人主观认为 * 代表读取整行数据,实际上 MySQL 优化器根本不会去读完整行;
  3. 没有区分“有无 WHERE 条件”,笼统下结论。

重点纠正:InnoDB 中,COUNT(*) 不会读取完整一行数据,优化器只利用索引条目数量来统计。

四、无WHERE条件的特殊场景

-- 查询整张表总条数SELECT COUNT(*) FROM `user`;SELECT COUNT(id) FROM `user`;

很多人发现这条 SQL 查询很慢。

原因在于:InnoDB 的事务多版本机制,无法缓存表总行数,无论 COUNT(*) 还是 COUNT(id),都必须扫描索引来统计,两者速度依旧基本一致。

若需高频查询表总量,可考虑使用 Redis 缓存或定时统计表总数,避免频繁 COUNT 扫描索引。

五、新增对比:COUNT(常量)

额外拓展一种写法:

SELECT COUNT(1) FROM `user_login_log` WHERE `date` = CURDATE();

在新版本 MySQL 中,COUNT(1) 会被优化器等价优化为 COUNT(*),性能同样持平。因此无需盲目推崇 COUNT(1)

六、一张表清晰区分三种写法

写法作用是否判NULL性能建议风险
COUNT(*)统计所有符合条件行不判断NULL推荐,官方标准
COUNT(id)统计id不为NULL的行判断NULL可用,略逊于COUNT(*)id必须为主键非空,否则结果异常
COUNT(login_ip)统计login_ip不为NULL的行判断NULL不推荐字段存在NULL时统计数量失真,开销更大

七、线上编码规范建议

优先使用 COUNT(*),遵循 SQL 标准,语义清晰,MySQL 官方推荐;

-- 标准写法SELECT COUNT(*) AS active_num FROM user_login_log WHERE `date` = CURDATE();

禁止使用 COUNT(普通业务字段) 统计表总行数;

如果确实需要统计“某字段不为空”的数据,才用 COUNT(字段名)

-- 合理场景:统计有登录IP的用户SELECT COUNT(login_ip) FROM user_login_log WHERE `date` = CURDATE();

不要为了“优化性能”把 COUNT(*) 强行改成 COUNT(id),这属于无效优化;

大表频繁全量 COUNT 统计,建议使用缓存预聚合方案。

八、实战验证方式

使用 EXPLAIN 对比两条 SQL 的执行计划即可直观感受:

EXPLAIN SELECT COUNT(*) FROM user_login_log WHERE `date` = CURDATE();EXPLAIN SELECT COUNT(id) FROM user_login_log WHERE `date` = CURDATE();

观察输出中的 type、key、rows,基本完全一致,可直观证明性能差距极小。

九、补充:MyISAM简要区分(了解即可)

MyISAM 引擎内部保存了表总行数:

SELECT COUNT(*) FROM `user`; -- 不加WHERE,瞬间返回

但只要带上 WHERE 条件,MyISAM 同样需要扫描数据,此时 COUNT(*)COUNT(id) 差距也不大。

新项目基本不会使用 MyISAM,此处仅作知识拓展。

十、全文总结

  1. InnoDB 引擎下,COUNT(*)COUNT(id) 性能几乎持平,COUNT(*) 理论小幅领先;
  2. 网传“COUNT(id) 速度更快”属于过时谣言,不要作为优化依据;
  3. 统计满足条件全部行数,统一使用 COUNT(*)
  4. 杜绝用 COUNT(普通字段) 统计总行数,存在逻辑 BUG 与性能损耗;
  5. SQL 优化应将重心放在索引设计,不要在 COUNT 写法上做无效内卷。

写代码记住一条准则:先保证语义准确,再追求性能。符合 SQL 标准的 COUNT(*) 是兼顾可读性与性能的最优选择。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。