GROUP BY变慢主因是执行计划出现Using temporary或Using filesort;前者表明MySQL建临时表存分组结果,后者代表额外排序,均因索引未覆盖过滤+分组+返回字段全链路所致。

GROUP BY 变慢,大概率是因为执行计划里出现了 Using temporary 或 Using filesort —— 这不是警告,是数据库已经放弃走索引、开始硬扛的明确信号。
怎么看执行计划里有没有临时表和文件排序
直接跑 EXPLAIN,盯住 Extra 列:
- 只要出现
Using temporary,说明 MySQL 正在内存或磁盘上建临时表来存分组中间结果;数据量一过几万行,就很容易溢出到磁盘,I/O 暴涨 - 出现
Using filesort,代表它得额外对 GROUP BY 字段做一次排序——哪怕你没写ORDER BY,MySQL 默认也会按分组键排,除非显式加ORDER BY NULL -
key列显示NULL或用了不匹配的索引,type是ALL或index(全索引扫描),基本等于没走有效路径
为什么 GROUP BY 会触发临时表而不是直接走索引
根本原因是:索引没覆盖“过滤 + 分组 + 返回字段”整条访问链。常见断点有:
-
GROUP BY a, b,但索引是(b, a)或(a)单列 —— 左前缀不匹配,无法利用索引天然有序性 -
SELECT a, b, SUM(c),索引只建了(a, b),c得回表查,MySQL 认为不如全扫+临时表聚合更省事 -
WHERE created_at > '2024-01-01'是范围查询,放在复合索引中间或开头,会让后续字段失效,GROUP BY user_id就没法接着用索引顺序 - 隐式类型转换:比如
user_id是BIGINT,但写成WHERE user_id = '123',索引直接失效,连带 GROUP BY 失去依托
ORDER BY NULL 真的有用吗
有用,但只解决表层症状,不治本:
- 加
ORDER BY NULL能干掉Using filesort,前提是执行计划里Using temporary已经消失 —— 否则只是少排一次序,临时表还在那儿吃内存 - 如果
Extra里同时有Using temporary和Using filesort,删掉ORDER BY NULL不会变快;加了也不会变快太多 - 某些客户端(比如 SQLyog)会偷偷优化掉排序逻辑,导致本地快、服务器慢,别被这种假象骗过去
MySQL 5.7 和 8.0 在这个问题上的关键差异
版本差异直接影响你能不能靠索引绕开临时表:
- MySQL 5.7 不支持函数索引,
GROUP BY DATE(created_at)没法直接建索引覆盖;你得先加生成列created_date,再建索引,否则必走临时表 - MySQL 8.0 支持
INDEX (DATE(created_at)),也支持降序索引,比如(status DESC, category ASC),能同时满足GROUP BY status, category和ORDER BY status DESC, category,避免双排序 - 8.0 的
EXPLAIN FORMAT=TREE能直接看到 “Using index for group-by”,5.7 只能靠Extra里没Using temporary+key非空来猜
真正卡住 GROUP BY 的,从来不是语法本身,而是索引是否让分组键的物理分布与查询逻辑对齐。一旦执行计划里 Using temporary 出现,说明数据库已经在用通用算法硬算,而不是靠结构加速——这时候调参数、换写法意义不大,得回到索引设计本身。

















