GROUP BY 执行时为什么慢?先看执行计划里的 Using filesort
和 Using temporary
MySQL 对
GROUP BY的默认实现,常会触发临时表(
Using temporary)和文件排序(
Using filesort),尤其当分组字段没索引、或 SELECT 列包含非分组/非聚合字段时。这不是 bug,是语义强制:SQL 标准要求
GROUP BY后的
SELECT列必须函数依赖于分组键,否则 MySQL 5.7+ 严格模式下直接报错
ERROR 1055。 检查执行计划:
EXPLAIN SELECT ... GROUP BY ...,重点看
Extra列是否出现
Using temporary; Using filesort避免
SELECT *或混用非分组字段(如
SELECT name, COUNT(*) FROM t GROUP BY dept_id中
name未被聚合也未在
GROUP BY中) 若业务真需某一分组内的任意值,显式用
ANY_VALUE(name),并确保该字段有索引支撑后续过滤
ORDER BY + GROUP BY 一起用,索引怎么建才不白搭?
当 SQL 同时含
GROUP BY a和
ORDER BY b,MySQL 无法复用同一个索引同时满足两者——除非
b是
a的后缀列(即联合索引顺序为
(a, b))。否则,
GROUP BY可能走
(a)索引完成分组,但
ORDER BY b仍要回表或排序。 最优索引策略:建
(a, b)联合索引,且确保
SELECT中所有非聚合字段都来自该索引覆盖范围(避免回表) 如果
ORDER BY是降序(
ORDER BY b DESC),MySQL 8.0+ 支持在索引中定义方向(
INDEX idx(a, b DESC)),5.7 及以前只能全升序,降序会失效
Using filesort注意:WHERE 条件字段应放在联合索引最左侧,例如
WHERE status=1 GROUP BY a ORDER BY b,索引应为
(status, a, b)
用 SQL_BIG_RESULT 提示强制走磁盘临时表反而更快?
当分组结果集很大(比如千万级唯一分组值),内存临时表(
tmp_table_size限制)频繁撑爆转成磁盘表,此时 MySQL 默认行为可能反复创建销毁内存表。加
SQL_BIG_RESULT提示会跳过内存试探,直接用磁盘临时表 + 排序算法,反而更稳定。 适用场景:
GROUP BY后预计行数远大于
tmp_table_size / avg_row_length,且服务器磁盘 I/O 压力可控 写法:
SELECT SQL_BIG_RESULT COUNT(*), dept_id FROM t GROUP BY dept_id副作用:绕过内存优化路径,小结果集反而变慢;需配合
sort_buffer_size调优排序性能
替代方案:用窗口函数或物化视图绕开 GROUP BY 性能瓶颈
MySQL 8.0+ 支持窗口函数,某些场景可把「先分组再取 Top N」逻辑改写为
ROW_NUMBER() OVER (PARTITION BY x ORDER BY y),避免中间临时表。而对高频聚合查询,用
CREATE TABLE AS SELECT ... GROUP BY预计算 + 定期刷新,比实时聚合更可靠。 例如:原查询
SELECT dept_id, MAX(salary) FROM emp GROUP BY dept_id ORDER BY MAX(salary) DESC LIMIT 10,可改用窗口函数避免排序全部分组结果 物化聚合表需注意数据一致性:用触发器、Flink CDC 或定时任务同步源表变更,不要依赖应用层双写 MySQL 8.0.23+ 支持不可见索引,可先建索引验证效果,再决定是否启用 真正卡住性能的,往往不是语法本身,而是分组键选择失当(比如用高基数字符串字段做
GROUP BY)、或没意识到
ORDER BY和
GROUP BY的索引需求本质冲突。先看
EXPLAIN,再动索引,最后才考虑提示或重构。
