alter table卡在waiting for table metadata lock是元数据锁(mdl)冲突,因ddl需mdl_exclusive锁而被未提交事务或sleep连接持有的mdl_shared_read锁阻塞;须用performance_schema.metadata_locks查lock_status='granted'定位持锁线程,并结合innodb_trx确认长事务。

ALTER TABLE 卡在 Waiting for table metadata lock 是什么锁
这不是行锁或表数据锁,是元数据锁(MDL)。只要一个会话对某张表执行过任何语句(哪怕只是 SELECT * FROM t),且事务未提交、连接未断开,它就会持有 MDL_SHARED_READ 锁;而 ALTER TABLE 必须拿到 MDL_EXCLUSIVE 锁才能开始,二者互斥。
常见诱因包括:
- 应用连接池中存在长期
Sleep状态的连接,且之前执行过查询但没显式COMMIT或ROLLBACK - 开发/运维在终端执行了
START TRANSACTION后忘记结束,或者直接关了窗口 - 监控脚本、定时任务执行了未加事务控制的
SELECT,连接保持打开
ALGORITHM=INPLACE 和 LOCK=NONE 并不等于“不锁”
MySQL 会按规则严格判断是否能走在线 DDL,失败就报错,不会静默降级。例如:
-
ADD COLUMN在末尾添加,且字段允许NULL、无DEFAULT值 → 5.6+ 可ALGORITHM=INPLACE - 同上但带
NOT NULL DEFAULT 'x'→ 必须全表回填,即使ALGORITHM=INPLACE也无效 -
MODIFY COLUMN改长度(如VARCHAR(255) → VARCHAR(1024))→ 5.7 中 100% 强制重建;8.0.12+ 仅当字符集、存储格式、长度头不变时才可能真正 inplace - 加唯一索引、全文索引、含
BLOB字段的列 →LOCK=NONE很可能被忽略,实际仍需短暂锁表
验证方式:执行前先加 EXPLAIN FORMAT=JSON 查看输出中 "alter_algorithm": "inplace" 和 "supports_inplace": true 是否同时为真。
如何快速定位谁在 hold 住 MDL
别只看 SHOW PROCESSLIST,它只能看到活跃连接。真正卡住 MDL 的,往往是那些 Command = Sleep、Time > 300、Info = NULL 的“幽灵连接”。正确做法是组合查:
-
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60→ 找出运行超 1 分钟的真实事务 -
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table'→ MySQL 8.0+ 直接看到谁持有什么级别的 MDL -
SELECT * FROM performance_schema.data_lock_waits→ 查谁在等谁的锁(8.0+)
注意:SHOW OPEN TABLES WHERE In_use > 0 对 MDL 无效,别依赖它。
生产环境改大表结构的底线操作
千万级以上表,禁止直连主库执行 ALTER TABLE。必须用绕过 MDL 的方案:
- 首选
pt-online-schema-change:它建影子表 + 触发器捕获变更 + 分块拷贝,全程原表可读写;但要求原表有主键或唯一非空索引,且不能已有触发器 - 次选从库先行:在从库执行
ALTER TABLE,再做主从切换;但要注意 binlog 格式(ROW 模式下新增列必须在末尾),且严禁只在从库跑、不切主从——否则主从结构不一致 - 手动影子表方案风险高:容易漏同步增量、rename 失败导致双写或丢失,仅限极小流量表临时救急
真正难的不是“怎么改”,而是“怎么确认没人正在悄悄读这张表”——很多锁表问题,根源是开发测试环境留下的未关闭连接,在凌晨三点突然苏醒并死死攥着 MDL 不放。











