直接kill alter进程无效,真正阻塞的是未提交的长事务或不兼容锁请求;须先通过show processlist和innodb_trx定位持锁源头,再kill线程回滚事务释放mdl锁。

直接 kill ALTER 进程没用,它只是被阻塞的受害者;真正卡住的是没提交的长事务或不兼容的锁请求。修复必须先定位持锁源头,再针对性处理。
查 Waiting for table metadata lock 到底卡在谁身上
看到大量线程状态是 Waiting for table metadata lock,别急着杀 ALTER,先找“占着 MDL 不放”的那个:
- 运行
SHOW PROCESSLIST;,重点关注State列为该状态、Time值远大于其他线程的连接(比如 > 300 秒) - 记下它的
ID(第一列),再查对应事务:SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX WHERE trx_mysql_thread_id = <id>;</id> - 如果
trx_state是RUNNING或LOCK WAIT,且trx_started时间很早,基本就是它——一个没提交的SELECT ... FOR UPDATE或开了事务后忘了COMMIT - 确认业务无影响后,执行
KILL <thread_id>;</thread_id>(不是KILL QUERY),只有完整 KILL 才能回滚事务、释放 MDL 锁
区分是长事务阻塞、DDL 耗时过长,还是死锁
同样报 Waiting for table metadata lock,但背后原因完全不同,处理方式也不同:
-
长事务阻塞:最常见。比如一个
START TRANSACTION; SELECT * FROM t WHERE id = 1 FOR UPDATE;后一直没COMMIT,ALTER 就永远等不到 MDL_EXCLUSIVE。查INNODB_TRX中老事务即可定位 -
DDL 自身耗时触发锁等待超时:比如对千万级表加
NOT NULL字段且没设默认值,MySQL 会全表更新填 NULL,持有行锁和 MDL 锁几分钟。错误日志里会出现Error 1205或超时提示,此时应改用ADD COLUMN ... DEFAULT ''或分步操作 -
高并发下 DDL 与写入形成死锁:MySQL 会主动报
Error 1213并回滚其中一个,但业务已受损。需看SHOW ENGINE INNODB STATUS\G中的LATEST DETECTED DEADLOCK段分析冲突路径
用 pt-online-schema-change 绕开 MDL 锁
生产环境大表 DDL,优先用 pt-online-schema-change,它不依赖 MySQL 原生 DDL 流程,而是通过影子表+触发器实现在线变更:
- 要求原表必须有主键或唯一非空索引,否则无法做增量同步
- 已有触发器的表不能用,会冲突
- 执行前务必加
--dry-run --print看它生成的 SQL,重点核对RENAME TABLE的顺序和目标库名是否正确 - 禁止在从库单独运行再切主从——
pt-osc不复制 DDL,会导致主从结构不一致 - 它生成的临时表名带
_pt_前缀,操作完成后自动清理;若中途失败,需手动删掉残留影子表
ALGORITHM=INSTANT 不是万能钥匙
MySQL 8.0.29+ 支持 ALGORITHM=INSTANT,但限制极严,误用反而导致失败:
- 只支持末尾加列、删非索引列、改列名/注释;改类型、加索引、加
NOT NULL都不支持 - 表格式必须为
DYNAMIC或COMPRESSED;可通过SHOW CREATE TABLE t;查ROW_FORMAT - 执行前必须用
SELECT @@version;确认是 ≥ 8.0.29,不是“2026 年发布版”这种模糊说法 - 即使满足所有条件,
ALTER TABLE t ADD COLUMN c INT成功,也不代表ALTER TABLE t MODIFY c BIGINT也能 INSTANT —— 类型变更永远不支持
真正容易被忽略的是:所有在线 DDL 方案都建立在 innodb_file_per_table = ON 基础上,如果关了,ibdata1 只增不减,空间问题永远解不开;另外,任何 DDL 操作前,务必确认 tmpdir 所在磁盘是 SSD 且空间充足,否则排序阶段 I/O 直接拖垮整条链路。











