GROUP BY必须建临时表是因为缺乏索引支持有序分组,需先写入临时表再排序聚合;触发Using temporary的典型情况包括分组字段不在索引最左前缀、WHERE含范围条件、分组字段使用函数、JOIN后分组字段非驱动表字段、SELECT含非聚合非分组未覆盖字段。

GROUP BY 为什么必须建临时表
因为 MySQL 默认需要先排序再分组,而排序的前提是数据按分组字段有序。如果 GROUP BY 字段上没有可用索引(或索引无法被有效利用),MySQL 就没法顺序扫描出“相同分组值连续排列”的数据流,只能退而求其次:把所有符合条件的行先捞出来,写进一张临时表,再对这张表按分组字段排序,最后遍历归并统计。
哪些情况一定会触发 Using temporary
不是“有没有索引”,而是“索引能不能直接支撑分组逻辑”。以下情况会强制走临时表:
-
GROUP BY字段未出现在任何索引的最左前缀位置(例如索引是(status, category),却执行GROUP BY category) -
WHERE条件含范围查询(>、BETWEEN、LIKE 'abc%'),破坏索引的天然有序性,使松散索引扫描失效 - 分组字段用了表达式或函数(如
GROUP BY user_id % 10、GROUP BY UPPER(name)),索引列无法直接比对 - 多表
JOIN后GROUP BY的字段不在驱动表上(EXPLAIN中type不是const或ref的那张表) -
SELECT中包含非聚合的非分组字段,且该字段未被索引覆盖(触发回表,MySQL 5.7+ 往往放弃松散扫描)
Using temporary 不等于一定慢,但落盘就是危险信号
内存临时表(MEMORY 引擎)本身开销可控;真正要警惕的是 Created_tmp_disk_tables 计数上升——说明已超出 tmp_table_size 和 max_heap_table_size 的较小值(默认 16MB),开始写磁盘临时表(InnoDB)。这时 I/O 成为瓶颈,性能断崖下跌。
查当前阈值:SHOW GLOBAL VARIABLES LIKE 'tmp_table_size';
观察是否落盘:SHOW STATUS LIKE 'Created_tmp_disk_tables';
ORDER BY NULL 是个关键开关,但常被忽略
即使你没写 ORDER BY,MySQL 也会对 GROUP BY 结果**默认按分组字段再排一次序**。这个隐式排序若无法利用索引,就会在临时表上额外触发 Using filesort —— 多一层排序,多一次全表扫描。
解决方法很简单:GROUP BY user_id ORDER BY NULL。加上这句,就能跳过二次排序,避免不必要的 Using filesort。
注意:ORDER BY NULL 只抑制排序,不改变分组逻辑,也不影响结果正确性。


















