Using temporary不是错误,而是MySQL因索引未覆盖GROUP BY、DISTINCT或ORDER BY字段而创建临时表的正常fallback;真正需解决的是索引缺失、顺序错配(如GROUP BY a却建INDEX(b,a))、函数包裹(如DISTINCT UPPER(name))或WHERE与分组字段不协同等问题,根治方法是建立严格匹配的覆盖索引,而非盲目调参。

Using temporary 不是错误,但说明 MySQL 正在用内存或磁盘临时表完成分组、去重或排序——真正要防的是它落盘,而不是它出现。
为什么加了索引 Still Using temporary
常见错觉:建了索引就万事大吉。实际中,GROUP BY a 却只建了 INDEX(b, a),MySQL 无法用该索引流式分组,因为 a 不在最左前缀;同理,DISTINCT UPPER(name) 必走临时表,函数包裹直接让索引失效。
- 单字段
GROUP BY a→ 必须有INDEX(a)或INDEX(a, ...),INDEX(x, a)无效 - 带条件
WHERE c = 1 GROUP BY a, b→ 最优索引是INDEX(c, a, b),顺序不能乱 -
ORDER BY字段若与GROUP BY不一致(如GROUP BY a ORDER BY b),再好的索引也白搭 - 执行
EXPLAIN前务必先ANALYZE TABLE,否则优化器可能因统计信息陈旧而误判索引可用性
tmp_table_size 和 max_heap_table_size 怎么设才有效
这两个值决定临时表能否留在内存里——MySQL 取二者中较小者作为上限。设一个、不设另一个,等于没设。
- 默认值常为
16777216(16MB),几万行聚合就容易撑爆,生产建议同步设为67108864(64MB)至268435456(256MB) - 临时生效:
SET SESSION tmp_table_size = 67108864和SET SESSION max_heap_table_size = 67108864 - 全局生效需写入配置文件,并重启或用
SET PERSIST;注意:对已建立的连接无效 - 必须监控
Created_tmp_disk_tables/Created_tmp_tables比值,持续高于 5% 就说明仍频繁落盘
哪些写法会悄悄触发 Using temporary
有些 SQL 看似简单,却因隐含逻辑强制建临时表,且不易被 EXPLAIN 主查询暴露。
-
UNION默认等价于UNION DISTINCT,哪怕两个子查询结果天然不重叠,也会合并后去重——优先用UNION ALL -
SELECT DISTINCT a, b FROM t ORDER BY c:c 与分组字段无关,MySQL 必须先去重再排序,临时表绕不开 - 子查询里的
GROUP BY或DISTINCT,主查询EXPLAIN的Extra列看不到Using temporary,得单独EXPLAIN子查询 -
SELECT user_id, viewed_user_sex FROM t GROUP BY viewed_user_age:违反 SQL 标准(非分组字段未聚合),MySQL 强制兜底用临时表
sort_buffer_size 调大有用吗
它和 Using temporary 是两套机制:sort_buffer_size 只管 ORDER BY 的内存排序缓冲,对 GROUP BY 完全无效。
- 默认
262144(256KB)常不够,可按需设为4194304(4MB)左右,但它是每个线程独占分配,别设到几百 MB,易触发 OOM - 即使调大,若
ORDER BY字段无索引、数据量远超缓冲,MySQL 仍会退回到外部排序,Using filesort不会消失 - 它不能消除
Using temporary,只能降低排序阶段的 I/O 开销;真正根除还得靠覆盖索引
最容易被忽略的一点:临时表是否落盘,只取决于 tmp_table_size 和 max_heap_table_size 中较小的那个值——哪怕你把前者设成 1GB,后者仍是默认 16MB,那临时表超过 16MB 就立刻写磁盘。参数再大,也填不满索引设计的漏洞。


















