优化GROUP BY需创建合适索引、避免隐式排序、减少数据量;建议为分组字段建联合索引,优先WHERE过滤,用ORDER BY NULL禁用排序,并通过EXPLAIN分析执行计划。

MySQL 执行 GROUP BY 时,**不先排序、也不固定用哈希或排序策略——它根据是否有可用索引、内存限制、SQL_MODE 和显式 ORDER BY 动态选择分组路径**。没索引就硬扫+建临时表;有索引就可能跳着读,省掉排序和临时表。
GROUP BY 没走索引时:全表扫描 + 内存/磁盘临时表
当 GROUP BY 字段无索引,或 WHERE 条件导致索引失效(比如 WHERE status != 'done'),MySQL 只能全表扫描。此时会:
- 逐行读取满足
WHERE的数据,提取分组列和聚合字段 - 在内存中构建哈希表:
key = 分组列值,value = 聚合中间状态(如 COUNT=1、SUM=xxx) - 若哈希表撑爆
tmp_table_size或max_heap_table_size,自动落盘为 MyISAM 临时表 - 最后遍历哈希表(或磁盘临时表)输出结果——**默认还会隐式按分组列排序**,除非加
ORDER BY NULL
典型 EXPLAIN 输出:Using temporary; Using filesort。这两个提示就是性能瓶颈信号。
GROUP BY 走了索引:可能跳过排序和临时表
前提是索引能覆盖分组顺序,且没有破坏有序性的操作(如范围查询、函数包裹)。常见有效场景:
- 联合索引
(a, b),查询GROUP BY a→ 可松散索引扫描(Loose Index Scan),每组只取第一条 - 联合索引
(a, b),查询GROUP BY a, b→ 紧凑索引扫描(Tight Index Scan),天然有序,无需额外排序 - 索引包含所有
SELECT字段(覆盖索引),还能避免回表
这时 EXPLAIN 中不会出现 Using temporary 或 Using filesort,执行计划里 Extra 可能是 Using index 或空。
为什么有时 GROUP BY 后结果顺序“看起来”是乱的?
这不是 MySQL bug,而是你触发了哈希分组路径,且没写 ORDER BY:
- 哈希表本身无序,遍历输出顺序取决于哈希桶分布,不可预测
- 即使走了索引,如果加了
HAVING过滤或SQL_BIG_RESULT提示,优化器也可能退回到临时表+排序路径 -
ONLY_FULL_GROUP_BY开启后,SELECT中非分组非聚合字段会被拒绝,但不影响分组逻辑本身
想确保顺序,必须显式写 ORDER BY;想禁用隐式排序节省开销,加 ORDER BY NULL。
最容易被忽略的细节:GROUP BY 的“分组键”必须完全匹配
分组不是按值相等判断,而是按表达式计算后的完整值做哈希或比较:
-
GROUP BY UPPER(name)和GROUP BY name是两套分组逻辑 -
GROUP BY DATE(created_at)会把同一天不同时间的数据归为一组,但无法利用created_at索引的有序性(除非建函数索引) - NULL 值在分组中被视为相同键,所有 NULL 自动归入同一组
真正影响性能的,从来不是“要不要用 GROUP BY”,而是“分组键能不能走索引 + 聚合过程要不要落盘”。索引设计不对,再调 tmp_table_size 也只是缓一时之痛。


















