COUNT(*) vs COUNT(1):别再吵架了,MySQL 8.0的执行计划早就告诉你了

简介: COUNT(*)和COUNT(1)哪个更快?这个问题在技术论坛上吵了十几年,答案从“一样快”到“COUNT(1)更快”到“COUNT(*)更快”反复横跳。每次MySQL发新版本,就有人拿出来重新测一遍。本文从执行计划、存储引擎、索引选择、优化器行为四个层面,完整拆解5种COUNT写法的语义差异、性能表现和适用场景。结论可能会让你意外——但更重要的是,你会理解为什么COUNT慢、怎么优化、什么场景该用什么写法。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

COUNT(*)COUNT(1)哪个更快?

这个问题在技术论坛上吵了十几年。我见过最极端的讨论,有人为了争这个在帖子里盖了200多楼。答案从“一样快”到“COUNT(1)更快”到“COUNT(*)更快”反复横跳。

2026年了,这个问题还值得吵吗?

不值得。因为答案早就有了——看执行计划。

但更值得问的问题是:如果你还在纠结这两个,说明你根本不理解COUNT真正的性能瓶颈在哪。

今天从执行计划、存储引擎、索引选择、优化器行为四个层面,把COUNT这件事彻底讲透。

一、COUNT的语义:5种写法的完整拆解

很多人的问题从一开始就问错了。他们问的是“性能”,但连语义都没搞清楚。

COUNT是聚合函数,计算一个SELECT结果集中的行数。但不同写法对“行”的定义完全不同:

COUNT(star)

  • 计算所有行数

  • 统计的是结果集中的每一行,不管列值是什么

  • 写法:COUNT(*)

  • NULL处理:计入所有行

COUNT(常量)

  • 计算所有行数(1是常量,每行都有值)

  • COUNT(*)在语义上等价

  • 写法:COUNT(1)COUNT(42)COUNT('hello')

  • NULL处理:计入所有行

COUNT(列名)

  • 计算指定列中非NULL值的行数

  • 只统计该列不为NULL的行

  • 写法:COUNT(column_name)

  • NULL处理:只计入非NULL的行

COUNT(DISTINCT 列名)

  • 计算指定列中非NULL值的去重后个数

  • 写法:COUNT(DISTINCT column_name)

  • NULL处理:不计入NULL

COUNT(star) OVER()

  • 窗口函数版本,计算当前窗口内的行数

  • 写法:COUNT(*) OVER(PARTITION BY ...)

  • NULL处理:计入所有行

写法 含义 是否统计NULL行
COUNT(*) 所有行数 ✅ 是
COUNT(1) 所有行数 ✅ 是
COUNT(col) 某列非NULL行数 ❌ 否
COUNT(DISTINCT col) 某列去重非NULL值个数 ❌ 否
COUNT(*) OVER() 窗口内行数 ✅ 是

关键结论COUNT(*)COUNT(1)在语义上完全等价——都是统计所有行数。它们在MySQL 8.0中的执行计划也完全一样。

二、COUNT的存储引擎原理:为什么InnoDB没有“总行数”?

这个问题是所有COUNT性能问题的根源。

MyISAM引擎:存储表的总行数,COUNT(*)直接返回,O(1)。

InnoDB引擎:不存储总行数,COUNT(*)需要实时扫描数据。因为InnoDB支持MVCC(多版本并发控制),不同事务在同一时刻看到的数据行数可能不同,无法缓存一个“总行数”。

所以在InnoDB里,COUNT永远是一个实时计算操作

那InnoDB怎么执行COUNT的?

  • 没有WHERE条件时:优化器会选择最小的索引(二级索引通常比聚簇索引小)进行扫描,快速估算行数,可能返回近似值。对于COUNT(*),优化器直接读取索引的元数据,不扫描数据行,所以很快——但这是近似值,不是精确值,且两者在8.0里执行路径相同。

  • 有WHERE条件时:必须根据WHERE条件扫描对应的索引或数据,精确计数

  • 精确COUNT永远需要扫描行数据:所谓的“快”只是相对于全表扫描而言,本质仍然是扫描操作。

三、执行计划分析:不同COUNT写法的真实差异

在1000万行数据的测试表上(有主键id,有二级索引status,status列有20%的NULL值):

场景1:无WHERE条件

写法 执行计划 扫描对象 优化器行为
COUNT(*) Select tables optimized away 无实际扫描 直接从统计信息读取估算行数
COUNT(1) Select tables optimized away 无实际扫描 同上,与COUNT(*)完全相同
COUNT(id) Select tables optimized away 无实际扫描 同上,主键索引统计信息
COUNT(status) 全索引扫描 status二级索引 实际扫描,统计非NULL行数

关键区别:

  • COUNT(*)COUNT(1)COUNT(主键)无WHERE条件时都走Select tables optimized away,直接从索引统计信息读取,几乎没有性能差异

  • COUNT(status)必须实际扫描status索引,统计非NULL的行数,需要扫描整个二级索引,是全量精确COUNT中最慢的写法之一(如果该列有大量NULL值,仍需扫描完整索引统计非NULL行数,无法跳过NULL)。

场景2:有WHERE条件

写法 执行计划 扫描对象 关键差异
COUNT(*) WHERE status=1 range status二级索引 只扫描status=1的行,无需回表
COUNT(1) WHERE status=1 range status二级索引 同上,优化器等价改写为COUNT(*)
COUNT(id) WHERE status=1 range status二级索引+回表 需要回表读取id
COUNT(status) WHERE status=1 range status二级索引 COUNT(*)相同

有WHERE条件时,COUNT(*)COUNT(1)的执行计划完全相同。 COUNT(列)如果列有索引,性能可能接近;如果列没有索引,则走全表扫描。

COUNT(列名)COUNT(*)的核心差异在于语义:前者统计非NULL的行数,后者统计所有行数。如果列上有大量NULL值,两者结果可能差很多——但性能差异取决于列是否有索引,而非写法本身。

四、优化器视角:MySQL到底是怎么选索引的?

对于COUNT(*),MySQL优化器的决策逻辑是:

  1. 有WHERE条件:选择WHERE条件中最优的索引进行扫描

  2. 无WHERE条件:在候选索引中选择最小的索引(索引树页数最少的)进行扫描

为什么选最小的索引?因为COUNT(*)只需要统计行数,不需要回表读取完整行数据。二级索引比聚簇索引小得多——二级索引只包含索引列+主键值,聚簇索引包含所有列。

重要结论COUNT(*)不会扫描聚簇索引(除非没有二级索引),因为二级索引的扫描成本更低。这也是为什么COUNT(*)在InnoDB中不是“全表扫描”——它是“全二级索引扫描”。

五、COUNT慢的5个真正原因

原因1:无索引的WHERE条件

这是最常见的场景:

-- status没有索引
SELECT COUNT(*) FROM orders WHERE status = 'PAID';

这会导致全表扫描(扫描聚簇索引),即使加了COUNT(*)也快不起来。

解决方案:在WHERE条件列上建索引。

原因2:需要精确计数的超大表

一张1亿行的表,任何精确COUNT都需要扫描大量数据。即使走二级索引,也要扫描上亿行索引数据。

解决方案:考虑用计数表(单独维护计数器)或Redis缓存

原因3:WHERE条件中使用了函数或隐式类型转换

-- 错误写法:对索引列用了函数
SELECT COUNT(*) FROM orders WHERE DATE(create_time) = '2026-09-01';

-- 错误写法:隐式类型转换(create_time是字符串)
SELECT COUNT(*) FROM orders WHERE create_time = 20260901;

这些写法都会导致索引失效,走全表扫描。

解决方案:确保WHERE条件中的列是原始列,类型匹配。

原因4:COUNT(DISTINCT)的排序开销

SELECT COUNT(DISTINCT user_id) FROM orders;

COUNT(DISTINCT)内部需要去重,通常需要排序或哈希操作,对大数据集来说很重。

解决方案:如果业务对“精确去重”要求不高,考虑使用APPROX_COUNT_DISTINCT()(MySQL 8.0.17+支持)或HyperLogLog。

原因5:行格式过大

如果表中有TEXTBLOB等大字段,行格式可能是动态的,读取成本更高。

解决方案:确保COUNT只走二级索引,避免回表。

六、4种COUNT优化方案

方案1:用小二级索引替代主键索引

当表没有WHERE条件时,MySQL选择最小的索引扫描。

如果一张表有主键(聚簇索引)和多个二级索引,优化器会选择叶子节点数量最少的索引。

-- 给一个占用空间小的列建二级索引
CREATE INDEX idx_small ON orders(status);
-- COUNT(*) 会优先扫描 idx_small 而不是主键

方案2:用计数表(Counter Table)

适合频繁计数的业务场景。

CREATE TABLE order_stats (
    stat_date DATE PRIMARY KEY,
    total_orders INT,
    paid_orders INT
);

每次插入/更新订单时,同步更新计数表。查询COUNT时直接读计数表,O(1)。

方案3:用EXPLAIN的估算行数

如果业务允许“近似值”,直接看EXPLAINrows字段。

EXPLAIN SELECT * FROM orders WHERE status = 'PAID';
-- rows列显示估算行数

EXPLAIN FORMAT=TREE提供了更详细的估算信息,包括每次操作的代价。

方案4:用分区表降低扫描范围

如果表按时间RANGE分区,COUNT只扫描相关分区。

-- 按月分区后 SELECT COUNT(*) FROM orders WHERE create_time >= '2026-09-01'; -- 只扫描9月分区

当然,分区表有额外的维护成本,需要权衡。

七、总结

COUNT(*) vs COUNT(1)的争论可以停了。它们在MySQL 8.0中执行计划完全相同。

真正值得关注的问题只有三个:

  1. WHERE条件能不能用到索引? → 不能就建索引

  2. 业务能不能接受近似值? → 能就用EXPLAIN估算

  3. 能不能用计数表缓存? → 能就单独维护

把精力放在这三个问题上,比争论COUNT(*)COUNT(1)哪个快有意义100倍。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
5天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1484 0
|
5天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1131 0
|
14天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3779 4
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
5天前
|
人工智能 安全 前端开发
刚刚 GPT-6 Astra 发布,全球最强,AGI 时代到来!
OpenAI 正式推出 GPT-6 Astra 模型,带大家看看这次 GPT 有哪些提升,跟 Claude Fable 5.1 有什么差距?AI 编程能力如何?AGI 真的来了么?
633 0
|
2天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
608 0
|
6天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)

热门文章

最新文章