应查sys.schema_table_lock_waits或performance_schema.metadata_locks定位mdl锁等待链,结合innodb_trx中trx_started识别持锁长事务,再通过trx_rows_modified判断是否可安全kill。

查谁在等、谁在堵,别只看 SHOW PROCESSLIST
看到 Waiting for table metadata lock 状态,第一反应不是杀掉那个 DDL 进程,而是它为什么卡着——真正堵住它的,往往是个没提交的 SELECT 或 UPDATE。仅靠 SHOW FULL PROCESSLIST 只能看到“等”,看不到“等谁”。State 列里时间长的线程未必是源头,有些只是空闲连接挂着,得结合 Info 和 Command 综合判断。
- 优先执行
SELECT * FROM sys.schema_table_lock_waits;(MySQL 5.7+),直接返回等待方、阻塞方、SQL、等待时长四要素 - 若无
sys库或权限受限,改用SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING';找出等待中的锁,再通过OBJECT_SCHEMA和OBJECT_NAME定位具体表 - 注意:
performance_schema.metadata_locks默认开启,但若被手动关闭过,需先确认:SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';,状态为YES才有效
定位持有 S MDL 的长事务,重点盯 trx_started
绝大多数元数据锁阻塞,源头不是 DDL,而是某个隐式开启却没提交的事务。比如 Python 应用用了连接池,执行完 SELECT 后没调 cursor.close() 或 conn.close(),连接复用但事务一直开着;或者 DBA 在客户端敲了 BEGIN 就去忙别的事了。
- 查活跃事务:
SELECT * FROM information_schema.innodb_trx WHERE trx_state = 'RUNNING' AND trx_started ,关键看 <code>trx_started时间戳,别只盯着trx_mysql_thread_id - 反查该线程正在执行什么:
SELECT THREAD_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_INFO FROM performance_schema.threads t JOIN performance_schema.events_statements_current e USING (THREAD_ID) WHERE t.PROCESSLIST_ID = ?;(把上一步查到的 ID 填进去) - 特别注意:
autocommit = 0的会话下,哪怕只执行一条SELECT,也会持 S MDL 锁直到COMMIT或ROLLBACK
杀之前先看有没有改数据,别一上来就 KILL
KILL 是最快释放锁的办法,但不是最安全的。如果那个长事务已经修改了数据(比如 UPDATE 了几十万行),强行中断可能导致数据不一致或回滚耗时极长,反而加剧阻塞。
- 先确认影响:
SELECT trx_id, trx_state, trx_isolation_level, trx_rows_modified FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = ?;,若trx_rows_modified > 0,说明有实际写入,需谨慎 - 若只是空闲读事务(
trx_rows_modified = 0),且确认无业务价值,再执行KILL ?;(填PROCESSLIST_ID,不是trx_mysql_thread_id) - 避免误杀:
KILL操作本身不加锁,但目标线程可能正处于关键路径(如正在刷 redo log),建议在低峰期操作
预防比排查更重要,lock_wait_timeout 得调
光靠事后 kill 解决不了根本问题。默认 lock_wait_timeout 是 31536000 秒(一年),意味着 DDL 会无限等待,一旦上游卡住,下游所有依赖该表的查询全被连锁拖死。
- 建议设为 3–10 秒:
SET GLOBAL lock_wait_timeout = 5;,让超时 DDL 主动报错ERROR 1205 (HY000): Lock wait timeout exceeded,而不是挂住整个链路 - 应用层配合:所有数据库连接必须设置
autocommit = 1,显式事务务必配对COMMIT/ROLLBACK - DDL 前必查:
SELECT * FROM information_schema.innodb_trx WHERE trx_started ,有结果就暂停变更
真正难处理的不是锁本身,而是那些看不见的隐式事务——它们不报错、不写日志、不释放锁,只安静地卡在那儿,等着你花半小时才定位到。











