Copying to tmp table 是 DDL 操作中引擎层重建表时的状态,表示 MySQL 正在将原表数据逐行拷贝到磁盘临时表(如 #sql-xxx),常见于不支持 INPLACE 的 ALTER TABLE、OPTIMIZE TABLE 等场景;其瓶颈在于磁盘空间、IO 性能或 MDL 锁等待,与 tmp_table_size 参数无关。

Copying to tmp table 是什么状态
这是 SHOW PROCESSLIST 中看到的一种线程状态,表示 MySQL 正在把原表数据逐行拷贝到一个临时表中——不是内存临时表,而是磁盘上的物理临时表(如 #sql-xxx 文件)。它出现在 DDL 操作中,比如 ALTER TABLE ... DROP INDEX、OPTIMIZE TABLE、ALTER TABLE ENGINE=InnoDB 等场景下。
关键点在于:这个状态 ≠ SQL 层的 CREATE TEMPORARY TABLE 或 GROUP BY 产生的临时表;它是 DDL 引擎层重建流程的一部分,走的是“新建结构 → 拷贝数据 → 原子替换”路径。
为什么不是 INPLACE,却退化成 Copying to tmp table
MySQL 5.6+ 支持 Online DDL,但是否启用 ALGORITHM=INPLACE 取决于操作类型和表当前状态。一旦不满足条件,就会自动降级为 ALGORITHM=COPY,触发完整拷贝流程。
-
DROP INDEX在某些情况下不支持 INPLACE:比如索引字段含TEXT/BLOB、表使用了旧版时间格式(如升级后未执行ALTER TABLE ... UPGRADE PARTITIONING)、或存在外键引用该索引 -
ALTER TABLE ENGINE=InnoDB永远不支持 INPLACE,无论版本——引擎切换必须重建,强制走 COPY - 表启用了
innodb_file_per_table = OFF,且目标引擎要求独立表空间时,也可能触发降级 - 显式指定
ALGORITHM=INPLACE但失败,错误码是ERROR 1845 (0A000),日志里会明确提示 “not supported for this operation”
这个状态背后的真实瓶颈在哪
很多人第一反应是调大 tmp_table_size,但这是无效的——该参数只控制 SQL 层内存临时表大小,和 DDL 的 #sql-xxx 临时表完全无关。
真正卡住的地方通常是:
- 磁盘空间不足:临时表文件默认写入
datadir下对应数据库子目录(如/var/lib/mysql/mydb/),不是tmpdir;df -h显示有空余,但可能 inode 耗尽、挂载点设了noexec、或云 RDS 启用了磁盘配额(如loose_rds_max_tmp_disk_space) - IO 延迟高:机械盘上拷贝千万级行,单线程顺序写极易成为瓶颈;
iotop或iostat -x 1能看到await持续 >50ms - MDL 锁等待被掩盖:如果前面有个长事务没提交,DDL 会卡在
Waiting for table metadata lock,但一旦拿到锁进入拷贝阶段,状态就变成Copying to tmp table——此时你看到的“正在拷”,其实是“终于开始干了”,而不是“刚排队成功”
如何规避或缩短这个状态持续时间
不能靠参数调优绕过,只能换方法或提前干预:
- 对大表,放弃原生
ALTER,改用gh-ost或pt-online-schema-change:它们在从库或影子表上做变更,主库 DML 不中断 - 确认是否真需要
OPTIMIZE TABLE:5.7+ 中多数情况只需ANALYZE TABLE更新统计信息;若为碎片整理,可先SET GLOBAL innodb_file_per_table = ON,再用ALTER TABLE ... ENGINE=InnoDB手动重建(仍需停写,但至少可控) - 执行前检查:
SELECT COUNT(*)确认行数、SHOW CREATE TABLE查字段类型(尤其是否有TEXT)、SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - TRX_STARTED) > 60清理长事务 - 监控指标重点关注:
Created_tmp_disk_tables(SQL 层)、Innodb_row_lock_time(行锁压力)、以及系统级df -i和lsof -p $(pidof mysqld) | grep -c 'tmp'(确认临时文件句柄数)
最常被忽略的一点:状态显示 Copying to tmp table 时,表已经处于不可写状态,且没有回滚机制——中断操作(Ctrl+C)可能导致表损坏或残留 #sql-xxx 文件,后续需手动清理并修复。


















