MySQL内存临时表大小由tmp_table_size与max_heap_table_size中较小值决定,二者必须设为相同值(如64M~256M),否则易触发磁盘落盘;监控Created_tmp_disk_tables与Created_tmp_tables比值超5%即表明内存不足,需同步调大两参数并优化SQL或索引以避免临时表。

Using temporary 不是报错,而是 MySQL 优化器明确告诉你:这条 SQL 必须建内部临时表来完成分组、排序或去重等操作。真正拖慢性能的,往往不是“用了临时表”,而是它**被迫落盘到磁盘**(即变成 MyISAM 或 InnoDB 磁盘临时表),引发大量 IO 和 CPU 消耗。
确认是否真被磁盘拖垮
先查监控指标,定位问题本质:
- 执行
SHOW GLOBAL STATUS LIKE 'Created_tmp%';,重点关注Created_tmp_disk_tables与Created_tmp_tables的比值 - 若
Created_tmp_disk_tables / Created_tmp_tables > 5%,说明多数临时表已落盘,内存不够用 - 同时观察
Slow_queries和Handler_read_rnd_next是否同步飙升,可佐证排序/分组阶段成为瓶颈
优先调参:扩大内存临时表上限
临时表能否留在内存,取决于 tmp_table_size 和 max_heap_table_size 中的较小值。二者必须同步调整:
- 生产环境建议设为
67108864(64MB)或268435456(256MB),避免默认 16MB 过小 - 执行:
SET SESSION tmp_table_size = 67108864; SET SESSION max_heap_table_size = 67108864; - 注意:该设置只对新建立的连接生效,不会影响已运行查询;也不回滚正在使用的临时表
根治方案:让 SQL 绕开临时表
参数调大只是缓解,真正高效的方式是改写 SQL 或补索引,让优化器无需建临时表:
-
GROUP BY 场景:确保分组字段有合适索引,且不带函数或隐式转换。例如
GROUP BY DATE(created_at)会失效,改用范围条件 +created_at索引 -
ORDER BY 场景:排序字段需被覆盖在同一个索引中,顺序严格匹配。如
ORDER BY status, created_at DESC,对应索引应为(status, created_at)(MySQL 8.0+ 支持混合方向) - DISTINCT + ORDER BY 混用:尤其当字段来自多表 JOIN 时,极易触发临时表。可拆分为两步:先用最小字段(如主键)完成排序分页,再用 IN 关联查详情
-
JOIN 中的非驱动表排序:若
ORDER BY字段不在驱动表上,又无合适索引,也会强制临时表。优先让驱动表包含排序字段,或为被驱动表补充联合索引
辅助诊断与验证
每次优化后,务必用 EXPLAIN 验证效果:
- 关注
Extra是否仍含Using temporary,以及是否同时出现Using filesort - 对比
rows值是否显著下降,key是否命中新创建的索引 - 开启慢日志并设置
log_queries_not_using_indexes = ON,捕获漏网之鱼


















