group by 变慢主因是触发全表扫描或磁盘临时表排序;需查执行计划、建合适复合索引、避免函数干扰,并用 count(distinct)、窗口函数等替代过度分组。

GROUP BY 为什么突然变慢了
多数时候不是 GROUP BY 本身慢,而是它触发了全表扫描或临时表排序。MySQL 8.0+ 默认用 hash_group_by=ON,但遇到大结果集或内存不足时会退回到磁盘临时表,IO 成为瓶颈;PostgreSQL 则依赖 work_mem,设太小直接走外排,性能断崖下跌。
- 先查执行计划:
EXPLAIN ANALYZE SELECT ... GROUP BY ...,重点看是否出现Using temporary; Using filesort(MySQL)或Sort Method: external merge(PostgreSQL) - 确认分组字段是否有索引:复合索引要满足最左前缀,比如
GROUP BY user_id, status需要(user_id, status)索引,单列user_id索引无效 - 避免在
GROUP BY字段上用函数:如GROUP BY DATE(created_at)会让索引失效,改用范围查询 + 预计算列
哪些聚合场景能绕开 GROUP BY
当只需要统计频次、去重数、极值等简单指标时,GROUP BY 往往是过度设计。用更轻量的替代方案能跳过排序阶段,减少内存和 CPU 消耗。
- 统计某字段不同值个数:不用
SELECT COUNT(*) FROM (SELECT DISTINCT col FROM t) _,改用COUNT(DISTINCT col)(MySQL 8.0+/PostgreSQL 支持松散索引扫描优化) - 取每个分组最新一条记录:别写
GROUP BY id HAVING MAX(updated_at),改用窗口函数ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY updated_at DESC)配合子查询 - 布尔聚合(如“是否存在”):用
EXISTS或BOOL_OR()(PostgreSQL)比GROUP BY+HAVING快得多
MySQL 的 SQL_BIG_RESULT 和 SQL_SMALL_RESULT 怎么选
这两个提示会影响优化器对临时表类型的决策,但效果高度依赖数据分布和版本。盲目加提示反而可能让执行计划更差。
-
SQL_BIG_RESULT强制用磁盘临时表 + 排序,适合分组后结果集远小于原表(例如 1000 万行聚合成 100 行),且内存不够存哈希表时 -
SQL_SMALL_RESULT强制用内存临时表 + 哈希聚合,适合分组键基数低、内存充足(tmp_table_size和max_heap_table_size足够) - 实测发现 MySQL 8.0.22+ 对
hash_group_by自适应更强,除非明确观察到Created_tmp_disk_tables暴增,否则不建议手动加提示
GROUP BY 后带 ORDER BY 的隐性开销
即使 ORDER BY 字段和 GROUP BY 完全一致,MySQL 仍可能额外排序——因为分组过程不保证输出顺序。PostgreSQL 更激进,默认按分组键排序,但一旦加了聚合函数(如 AVG()),就又得重排。
- 如果业务只要求「按分组键顺序返回」,MySQL 加
ORDER BY NULL可显式禁用排序;PostgreSQL 加ORDER BY GROUPING SETS或确保GROUP BY字段含主键前缀 - 避免
GROUP BY a ORDER BY b(b 不在分组字段中),这必然触发二次排序,且无法用索引覆盖 - 聚合后还要分页?
LIMIT放在子查询里,而不是整个GROUP BY外层,否则可能扫完全部分组才截断
真正卡住性能的,往往不是 GROUP BY 语法本身,而是它背后暴露的索引缺失、数据倾斜、或内存配置与实际负载错配。调优时盯着 EXPLAIN 里的 rows 和 Extra 字段,比背口诀有用得多。











