MySQL 是使用 COUNT(id) 还是 COUNT(*) 效率更高?

发布时间:2026/7/25 6:53:29
MySQL 是使用 COUNT(id) 还是 COUNT(*) 效率更高? 标签MySQL、InnoDB、COUNT优化、索引原理、SQL规范前言开发工作中统计行数经常会写出两种写法SELECTCOUNT(*)FROMuser_login_logWHEREdateCURDATE();SELECTCOUNT(id)FROMuser_login_logWHEREdateCURDATE();网上充斥大量老旧传言COUNT(*)需要扫描整行数据COUNT(id)只读取主键COUNT(id)速度更快。这条说法放到现在InnoDB 引擎下是错误谣言。本文结合 InnoDB 底层原理讲清楚COUNT(*)、COUNT(id)、COUNT(普通字段)的差异给出生产环境标准编码规范。前置环境本文全部基于 MySQL InnoDB5.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的记录。⚠️ 风险两点如果字段允许NULL统计结果和真实行数不一致产生业务BUGInnoDB需要取出字段真实值判断NULL开销高于 COUNT(*)。二、核心结论InnoDB带WHERE条件统计时COUNT(*) 和 COUNT(id) 性能几乎没有差距。不要耗费精力纠结二者选择二者执行计划、扫描行数、IO开销基本持平。底层原因InnoDB二级索引叶子节点本身就存放主键id。无论优化器选择二级索引扫描计数COUNT(*)只需要计数索引条目不需要读取字段值COUNT(id)除了计数还要额外取出id值做非空判断理论上COUNT(*)会略微优于 COUNT(id)只是绝大多数场景差距感知不到。三、误区拆解为什么会流传 COUNT(id) 更快谣言来源大多是老旧MyISAM认知混淆以及早期网络文章以讹传讹MyISAM无WHERE条件COUNT(*)超快引擎缓存总行数但MyISAM早已不是主流很多人主观猜想*代表读取整行数据实际上MySQL优化器根本不会读取完整行没有区分「有无WHERE条件」笼统下定论。重点纠正InnoDB中COUNT(*) 不会读取完整一行数据优化器只利用索引条目数量统计。四、无WHERE条件的特殊场景-- 查询整张表总条数SELECTCOUNT(*)FROMuser;SELECTCOUNT(id)FROMuser;很多人发现这条SQL查询很慢。原因InnoDB事务多版本机制没有办法缓存表总行数无论COUNT(*)/COUNT(id)都必须扫描索引统计二者速度依旧基本一致。想要高频查询表总量提速使用Redis缓存、定时统计表总数避免频繁COUNT扫描索引。五、新增对比COUNT(常量)额外拓展一个写法SELECTCOUNT(1)FROMuser_login_logWHEREdateCURDATE();在新版本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官方推荐-- 标准写法SELECTCOUNT(*)ASactive_numFROMuser_login_logWHEREdateCURDATE();禁止使用 COUNT(普通业务字段)统计表总行数如果需要统计「某字段不为空」的数据才使用COUNT(字段名)-- 合理场景统计有登录IP的用户SELECTCOUNT(login_ip)FROMuser_login_logWHEREdateCURDATE();不要为了“优化性能”把COUNT(*)强行改成COUNT(id)属于无效优化大表频繁全量COUNT统计使用缓存预聚合方案。八、实战验证方式使用EXPLAIN对比两条SQL执行计划EXPLAINSELECTCOUNT(*)FROMuser_login_logWHEREdateCURDATE();EXPLAINSELECTCOUNT(id)FROMuser_login_logWHEREdateCURDATE();观察输出type、key、rows基本完全一致可以直观证明性能差距极小。九、补充MyISAM简要区分了解即可MyISAM引擎内部保存表总行数SELECTCOUNT(*)FROMuser;-- 不加WHERE瞬间返回但只要带上WHERE条件MyISAM同样需要扫描数据此时COUNT(*)与COUNT(id)同样差距不大。新项目基本不会使用MyISAM仅作知识拓展。十、全文总结InnoDB引擎下COUNT(*)与COUNT(id)性能几乎持平COUNT(*)理论小幅领先网传「COUNT(id)速度更快」属于过时谣言不要作为优化依据统计满足条件全部行数统一使用COUNT(*)杜绝用COUNT(普通字段)统计总行数存在逻辑BUG与性能损耗SQL优化把重心放在索引设计不要在COUNT写法上做无效内卷。写代码记住一条准则先保证语义准确再追求性能符合SQL标准的COUNT(*)是兼顾可读性与性能的最优选择。