“Copying to tmp table”表示MySQL正将中间结果写入内存临时表,常见于GROUP BY、ORDER BY等操作;根本原因是内存不足(tmp_table_size与max_heap_table_size较小值受限)或缺失索引导致无法避免临时表。

为什么会出现 Copying to tmp table 状态
这个状态表示 MySQL 正在把中间结果写入内部临时表(in-memory 或 on-disk),常见于 GROUP BY、ORDER BY、DISTINCT、UNION、子查询等操作。它本身不是错误,但耗时长说明临时表过大或落盘频繁——根本原因通常是内存不足或排序/分组字段没走索引。
tmp_table_size 和 max_heap_table_size 要一起调
MySQL 用这两个参数共同限制内存中临时表的大小,取其中较小值。只改一个没用,必须同步调整:
-
tmp_table_size控制 HEAP 表上限(对 MEMORY 引擎临时表) -
max_heap_table_size控制用户创建的 MEMORY 表 + 内部临时表的内存上限 - 若中间结果超限,MySQL 自动转为磁盘临时表(MyISAM 或 InnoDB),I/O 开销剧增
- 建议线上先设为
256M(需结合可用内存评估),并观察Created_tmp_disk_tables是否下降
用 EXPLAIN FORMAT=TREE 看是否真需要临时表
传统 EXPLAIN 只显示 Using temporary,但看不出是 GROUP BY 还是 ORDER BY 引发的;FORMAT=TREE 能定位具体算子:
EXPLAIN FORMAT=TREE SELECT user_id, COUNT(*) FROM logs GROUP BY user_id ORDER BY COUNT(*) DESC;
输出里如果看到 > Sort: COUNT(*) DESC 前紧跟着 > Aggregate using temporary table,说明是 GROUP BY 触发的;若排序在聚合前出现,则可能是 ORDER BY 字段无索引导致回表+排序膨胀。
- 检查
GROUP BY字段是否有合适索引(复合索引注意最左前缀) - 避免
SELECT *后再GROUP BY,只选必要字段 - ORDER BY 中含函数(如
ORDER BY UPPER(name))会强制使用临时表
磁盘临时表引擎从 MyISAM 切到 InnoDB
MySQL 5.7+ 默认仍用 MyISAM 做磁盘临时表,但它不支持行级锁、崩溃恢复弱、且对大 BLOB/TEXT 更慢。通过设置 internal_tmp_mem_storage_engine=INNODB 可切换:
- 该变量仅影响磁盘临时表的存储引擎(内存临时表仍用 TempTable 引擎)
- 需确保
innodb_buffer_pool_size足够,否则反而加重压力 - 搭配
innodb_temp_data_file_path把临时表文件放在高速盘(如 NVMe)上 - 注意:8.0.16+ 才支持此变量,旧版本只能靠调大内存阈值来规避落盘
真正卡住的点,往往不在参数调多大,而在于没意识到 GROUP BY 字段缺失索引,或 ORDER BY 和 SELECT 字段不一致导致无法利用覆盖索引——这些比调 tmp_table_size 有效十倍。


















