GROUP BY字段必须是索引最左前缀,否则无法利用索引有序性分组,将触发Using temporary;ORDER BY字段须紧接GROUP BY字段且顺序方向严格一致,否则失效;覆盖索引需包含所有SELECT非聚合字段,避免回表。

GROUP BY字段必须是索引最左前缀
MySQL对 GROUP BY 的优化依赖索引的有序性:它会按索引顺序逐行扫描,遇到相同分组键时直接聚合,避免建临时表。如果索引不是以 GROUP BY 字段开头,这个机制就失效。
常见错误是把高频排序字段放前面、分组字段放后面,例如建了 INDEX idx_time_dept (create_time, department),但查询是 GROUP BY department ORDER BY create_time —— 此时 department 不在最左,索引完全无法用于分组,必然触发 Using temporary。
- 正确做法:索引首列必须严格匹配
GROUP BY列,如GROUP BY department→ 索引必须以department开头 - 若还有
WHERE条件(如WHERE status = 'active'),且该字段区分度高,可放在最左,但前提是它也参与分组或排序逻辑,否则可能割裂索引有效性 - 复合索引中,
GROUP BY后续列只能是「被聚合字段的排序依据」或「ORDER BY字段」,不能随意追加无关列
ORDER BY字段必须紧接在GROUP BY字段之后且顺序一致
当 GROUP BY a ORDER BY b 时,MySQL 无法复用同一个索引完成两件事:分组靠 a 有序,排序靠 b 有序,而 B+ 树索引只有一条有序路径。只有 b 是索引中 a 的**紧邻后缀**,且方向一致,才可能让排序“顺带”完成。
例如表有 INDEX idx_dept_hire (department, hire_date),以下查询能免 Using filesort:
SELECT department, MAX(hire_date) FROM employees GROUP BY department ORDER BY department, hire_date DESC;
但换成 ORDER BY hire_date DESC 单独出现,或 ORDER BY department ASC, hire_date ASC 而索引是 (department, hire_date DESC),就会失效。
- MySQL 5.7 及以前:索引定义中只要有一个
DESC,整个排序就退化为filesort;8.0+ 支持混合方向,但必须显式声明,如CREATE INDEX ... (department ASC, hire_date DESC) - 不能跳过中间列:索引是
(a, b, c),GROUP BY a ORDER BY c无效;ORDER BY b, c有效,ORDER BY c无效 -
ORDER BY中出现计算字段(如ORDER BY COUNT(*))或表达式(如ORDER BY UPPER(name)),索引必然失效
覆盖索引要包含SELECT中所有非聚合字段
即使 GROUP BY 和 ORDER BY 都走索引,如果 SELECT 里有未被索引覆盖的字段(比如 SELECT department, name, COUNT(*) 但索引只有 (department)),MySQL 仍需回表查 name,导致 I/O 上升,甚至让优化器放弃使用该索引。
典型陷阱是 SELECT * 配合 GROUP BY —— 这不仅违反 ONLY_FULL_GROUP_BY 模式,更让覆盖索引失去意义。
- 只选必要字段:
SELECT department, COUNT(*) FROM t GROUP BY department,索引(department)就够 - 若需额外字段(如
SELECT department, MAX(salary), MIN(age)),索引应为(department, salary, age),确保聚合函数能利用索引有序性(MAX/MIN在有序列上是 O(1) 查找) - 注意字段顺序:
MAX(salary)要求salary在索引中位于department之后,且不能被范围条件(如WHERE salary > 5000)中断
EXPLAIN里同时出现Using temporary和Using filesort意味着索引设计失败
这是最明确的信号:MySQL 既没用索引分组,也没用索引排序,而是老老实实建临时表 + 文件排序。此时别调参,先看索引。
检查步骤很简单:
- 运行
EXPLAIN FORMAT=TRADITIONAL your_query,重点盯key(是否用了预期索引)、key_len(用了索引几列)、Extra(是否有Using temporary; Using filesort) - 确认
GROUP BY字段是否出现在索引最左,且没有被WHERE中的范围条件(>,BETWEEN,IN多值)截断后续列 - 确认
ORDER BY字段是否为索引中GROUP BY字段的连续后缀,且 ASC/DESC 完全匹配
真正难处理的是聚合后还要按计算结果排序,比如 ORDER BY COUNT(*) DESC —— 这种情况索引无解,得接受 filesort,或改用物化视图、应用层缓存聚合结果。


















