group by 性能差异关键在于是否命中索引:无索引时触发磁盘临时表(using temporary)和 filesort,导致io瓶颈;复合索引需覆盖group by、where及select非聚合列;避免函数分组、隐式类型转换;order by与group by一致可复用有序性,否则额外排序;sort_buffer_size宜设2m~4m,过大引发oom;tmp_table_size与max_heap_table_size需同步调大以避免磁盘临时表;开启only_full_group_by可防止低效执行。

GROUP BY 为什么有时快有时慢?关键在是否走索引
MySQL 执行 GROUP BY 时,性能差异往往不是 SQL 写得“好不好”,而是执行计划有没有命中索引。如果 GROUP BY 字段上没有合适索引,MySQL 就必须回表或全表扫描后做临时表聚合,这时候就极易触发磁盘临时表(Using temporary),IO 成为瓶颈。
常见错误现象:EXPLAIN 显示 Extra 列含 Using temporary; Using filesort,且查询耗时随数据量非线性增长。
- 复合索引要覆盖
GROUP BY字段 +WHERE过滤字段 +SELECT中的非聚合列(如需MIN()/MAX()等) - 避免在
GROUP BY中用函数或表达式,例如GROUP BY YEAR(create_time)会强制放弃索引 - 注意隐式类型转换:比如
user_id是BIGINT,但WHERE user_id = '123'传字符串,可能导致索引失效,连带影响分组效率
ORDER BY + GROUP BY 混用时,排序到底在哪一步发生?
很多人以为 ORDER BY 是最后才排,其实不然。当 GROUP BY 和 ORDER BY 字段一致(如 GROUP BY category ORDER BY category),MySQL 可能复用分组过程中的有序性,跳过二次排序;但如果 ORDER BY 字段不在分组键中,或顺序不一致(如 GROUP BY a ORDER BY b),就必须额外走一次排序流程——这时就轮到 sort_buffer_size 发挥作用了。
典型表现:EXPLAIN 中 Extra 出现 Using filesort,且 Handler_read_rnd_next 值飙升,说明大量随机 IO。
- 若
ORDER BY字段有索引,优先让优化器走索引排序,而不是依赖sort_buffer -
GROUP BY ... ORDER BY NULL可显式禁用排序,避免无谓开销(适用于只关心分组结果、不要求输出顺序的场景) - 5.7+ 版本中,
GROUP BY默认隐含ORDER BY,如不需排序,务必显式加ORDER BY NULL
sort_buffer_size 不是越大越好,超限反而降速
sort_buffer_size 是每个连接独享的内存区,用于单次排序(包括 ORDER BY、DISTINCT、部分 GROUP BY 场景)。设成 256MB 看似爽,但实际可能拖垮整体性能。
原因很实在:该内存不能复用,每起一个新连接就分配一份;若并发高,容易触发 OS 内存交换(swap),甚至 OOM killer 杀进程。
- 线上建议初始值设为
2M~4M,观察Sort_merge_passes状态变量:持续 > 0 表示频繁归并排序,可小幅上调(每次 +1M) -
Sort_merge_passes / Questions比值超过 0.001,大概率说明排序内存不足,正在反复归并 - 注意区分
sort_buffer_size和read_rnd_buffer_size:后者影响回表随机读,调它对分组排序基本无效
真正卡顿的元凶常藏在“隐式分组”和临时表引擎里
有些 GROUP BY 查询看似简单,却莫名变慢,查 SHOW PROCESSLIST 发现状态长期卡在 Creating tmp table 或 Copying to tmp table on disk。这不是 SQL 问题,而是 MySQL 自动选用了磁盘临时表。
触发条件包括:结果集含大字段(TEXT/BLOB)、GROUP BY 字段长度超 tmp_table_size 与 max_heap_table_size 的较小值、或使用了不支持内存表的函数(如 JSON_EXTRACT)。
- 检查当前设置:
SELECT @@tmp_table_size, @@max_heap_table_size;,二者需同步调大(建议不超过 256M) - 避免在
SELECT列中返回未参与分组又没聚合的列(即违反 ONLY_FULL_GROUP_BY 但被关闭时),这会让 MySQL 强制建临时表 - 5.7+ 默认开启
ONLY_FULL_GROUP_BY,别轻易关——它不只是语法校验,也阻止了歧义导致的低效执行路径
分组排序的波动,很少来自算法本身,更多是执行路径在内存/磁盘、索引/全表、临时表引擎之间的临界切换。盯住 EXPLAIN、SHOW STATUS LIKE 'Sort%' 和 Handler_% 这三类指标,比盲目调参管用得多。











