ALTER TABLE 被卡住是因为元数据锁(MDL)由 Server 层管理,不体现在 INNODB_TRX 中;即使只执行 SELECT 未提交,也会持有 MDL 读锁阻塞 DDL,需通过 SHOW PROCESSLIST 或 performance_schema.metadata_locks 定位并 KILL CONNECTION 解决。

为什么 ALTER TABLE 会被卡住,查 information_schema.INNODB_TRX 却没看到长事务?
因为元数据锁(MDL)不走 InnoDB 事务系统,它由 Server 层管理,即使事务只执行了 SELECT、没修改数据,只要没提交,就可能持有 MDL 读锁,阻塞后续 DDL。常见现象是:DDL 命令卡在 Waiting for table metadata lock,但 INNODB_TRX 里查不到活跃事务。
- 真正持有锁的可能是隐式开启的自动提交事务,比如客户端连上后直接执行了
SELECT * FROM t1就没再操作,连接还开着 -
SHOW PROCESSLIST更可靠——重点看State列是否为Waiting for table metadata lock,同时检查Command是否为Sleep且Time很大 - MySQL 5.7+ 可查
performance_schema.metadata_locks(需提前开启performance_schema并设置setup_consumers),能直接看到谁在持有什么级别的 MDL
如何快速定位并杀掉持有 MDL 的会话?
别依赖 INNODB_TRX,直接从连接维度下手。核心逻辑是:找那些状态异常、空闲时间长、又恰好在目标表上有未释放锁的连接。
- 先用
SELECT THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO, PROCESSLIST_TIME FROM performance_schema.threads WHERE PROCESSLIST_COMMAND = 'Sleep' AND PROCESSLIST_TIME > 60;找出空闲超 1 分钟的连接 - 结合
sys.schema_table_lock_waits(MySQL 5.7+ 自带视图)查具体阻塞链:SELECT * FROM sys.schema_table_lock_waits WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'table_name'; - 确认后用
KILL CONNECTION <code>PROCESSLIST_ID; 终止——注意是KILL CONNECTION,不是KILL QUERY,后者只停当前语句,锁还在
ALTER TABLE 加上 ALGORITHM=INSTANT 就不会被阻塞?
不会。INSTANT 算法只跳过行数据拷贝和重建,但它依然需要获取 MDL 写锁(MDL_EXCLUSIVE),而写锁必须等所有读锁(包括普通 SELECT 持有的)释放才能拿到。所以 INSTANT 解决的是性能问题,不是锁冲突问题。
- INSTANT 仅支持添加列(非首列)、删除列、重命名列、修改列默认值;其他操作如加索引、改类型、删主键,仍走 COPY 或 INPLACE,同样要抢 MDL
- 即使操作本身支持 INSTANT,若表上有未提交的事务(哪怕只是
START TRANSACTION; SELECT ...;),DDL 依然会卡住 - MySQL 8.0.12+ 才完整支持 INSTANT,低版本设了也自动降级,不会报错但失去预期效果
线上不敢随便 KILL,有没有更温和的预防方式?
有,关键是控制“读操作”的生命周期和权限边界。MDL 冲突本质是读写并发失控,不是 DDL 本身有问题。
- 应用层避免长连接 + 长事务组合:PHP/Python 等脚本类服务,确保每次请求结束后显式关闭数据库连接,不要复用到下个请求
- DBA 层设置
wait_timeout和interactive_timeout到合理值(比如 300 秒),让空闲连接自动断开,比手动KILL更可控 - 对运维账号限制权限:禁止在生产库用普通账号执行交互式
SELECT;DDL 操作统一走审批平台,平台自动检测metadata_locks状态并提示风险
真正麻烦的不是锁本身,而是那些开着连接却不说话的“幽灵会话”——它们不报错、不超时、不释放锁,只安静地堵死所有 DDL。处理前多看一眼 PROCESSLIST 的 Time 和 State,比反复猜事务更省时间。










