直接杀掉持锁的长事务连接而非等待其结束,因alter table卡在waiting for table metadata lock时多由未提交事务或sleep连接死握mdl读锁导致;需查innodb_trx中trx_state='running'且运行超5分钟、trx_query为空或仅含select的记录;kill query无效,必须kill pid释放锁;instant算法仍需获取mdl-x锁,无法规避初始锁等待。

直接杀掉持锁的长事务连接,而不是等它自己结束——因为ALTER TABLE卡在Waiting for table metadata lock时,90%以上是某个未提交事务或Sleep连接在后台死握MDL读锁不放。
查INNODB_TRX里真正持锁的长事务
别只看SHOW PROCESSLIST里Command = 'Sleep'的连接,很多阻塞源状态是Sleep但trx_state = 'RUNNING',且trx_started早于当前时间5分钟以上。执行:
SELECT trx_id, trx_mysql_thread_id, trx_started, trx_state, trx_query FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 300;
- 重点筛
trx_query为空、或只有SELECT、SELECT ... FOR UPDATE的记录——这类事务大概率是BEGIN后忘了COMMIT -
trx_state = 'RUNNING'比'LOCK WAIT'更危险:说明它没在等锁,而是在“占着茅坑”持有MDL - 如果
trx_query显示UPDATE且TRX_ROWS_MODIFIED > 0,先确认业务影响再动手
用sys.schema_table_lock_waits快速定位谁在堵谁
MySQL 5.7+最省力的等待链视图,一行看清DDL被谁卡住:
SELECT * FROM sys.schema_table_lock_waits\G
- 关注
waiting_pid(卡住的ALTER TABLE线程)和blocking_pid(真正持锁的线程ID) -
sql_kill_blocking_connection字段可直接复制执行,避免手误输错PID - 如果
sys库不可用,立刻切到performance_schema.metadata_locks方案,别硬等 - 注意:该视图依赖
performance_schema.setup_instruments中wait/lock/metadata/sql/mdl必须为ENABLED
KILL CONNECTION才有效,KILL QUERY纯属浪费时间
KILL QUERY <code>pid只中断当前语句,事务仍活跃,MDL锁纹丝不动。必须用KILL <code>pid才能释放锁:
- 先查
information_schema.PROCESSLIST确认目标线程Command = 'Sleep'且Time > 300 - 再核对
INNODB_TRX中对应trx_state = 'RUNNING'且trx_query为空 - 满足以上两点,
KILL QUERY无意义,直接KILL <code>pid - 注意:
KILL会触发回滚,若该事务已写大量undo日志,回滚本身可能耗时数分钟——这期间仍阻塞其他DDL
ALGORITHM=INSTANT不是万能解药,得看操作类型
MySQL 8.0.12+的INSTANT算法只适用于极少数场景,加列必须满足三个条件:加在末尾、非NOT NULL DEFAULT、表已有主键。否则自动降级为INPLACE甚至COPY:
-
ALTER TABLE t ADD COLUMN c1 INT→ 可INSTANT -
ALTER TABLE t ADD COLUMN c1 INT NOT NULL DEFAULT 0→ 强制COPY(需全表回填) -
ALTER TABLE t MODIFY COLUMN c1 VARCHAR(512)→ 8.0.12+仍可能INPLACE,但8.0.11及更早必COPY - 执行前务必用
EXPLAIN FORMAT=JSON查"alter_algorithm": "instant"是否真实生效
真正容易被忽略的是:哪怕用了INSTANT,它仍需在语句开始前获取MDL-X锁——只要表上有未提交事务,ALTER依然会卡在Waiting for table metadata lock,只是锁持有时间极短而已。











