group by触发using temporary和using filesort的根本原因是索引未有效支撑分组:必须满足索引字段顺序严格匹配group by子句,且覆盖where条件和select非聚合列;单列索引通常无效,order by null可消除隐式排序但不解决临时表问题。

GROUP BY 触发 Using temporary 和 Using filesort 不是配置没调好,而是 MySQL 无法按分组字段的物理顺序流式读取数据——它只能先把所有匹配行捞进来,再建临时表、再排序、再分组。
为什么索引没被“真正用上”
常见错误是以为建了单列索引就万事大吉。实际上,GROUP BY 要跳过临时表,必须满足两个硬条件:索引字段顺序严格匹配 GROUP BY 子句,且 WHERE 条件和 SELECT 非聚合列都被该索引覆盖。
-
GROUP BY region, city需要INDEX(region, city);INDEX(city, region)完全无效 - 带
WHERE status = 1时,索引得是(status, region, city),把等值过滤字段放最左 - 如果
SELECT region, city, COUNT(*),索引(region, city)就够;但若查name,就得扩展成(region, city, name) - 隐式类型转换(如
user_id = '123'对比BIGINT)会让整个索引失效,EXPLAIN里key可能还显示用了索引,但Extra一定有Using temporary
为什么 ORDER BY NULL 必须加
MySQL 5.7+ 虽已移除 GROUP BY 的隐式排序语义,但优化器仍可能因“预期你要排序”而放弃更优索引路径。显式写 ORDER BY NULL 是唯一能明确告诉它“别排”的方式。
- 不加时,即使索引结构正确,也可能触发
Using filesort - 加了之后,只要索引覆盖到位,
EXPLAIN的Extra会干净地变成Using index - 注意:它只对
Using filesort有效,对Using temporary无效——后者说明分组逻辑本身还没跑通
为什么调 sort_buffer_size 没用
sort_buffer_size 只控制 ORDER BY 排序阶段的内存用量,和 GROUP BY 是否建临时表完全无关。真正决定临时表是否落盘的是 tmp_table_size 和 max_heap_table_size 中的较小值。
- 这两个参数必须设为相同值,否则只改一个等于白改
- 哪怕只超 1 字节,MySQL 就会把整个临时表刷到磁盘,生成
/tmp/#sql_*文件 - 调大参数只是延缓崩溃,不是修复;线上高频慢查询,90% 的根因是索引设计错位,不是内存不够
- 检查是否真写磁盘:用
SHOW PROFILE FOR QUERY N,重点看Copying to tmp table on disk这一行耗时
最容易被忽略的一点:即使 GROUP BY 字段上了单列索引,只要查询里带 WHERE 条件或 SELECT 其他字段,这个单列索引大概率就失效了——它没法同时覆盖过滤、分组和投影三个环节。










