应优先排查Command='Sleep'、State为空、Time>300秒的连接,通过INNODB_TRX和metadata_locks确认其持有SHARED_WRITE/EXCLUSIVE MDL锁,再分步执行KILL QUERY与KILL避免回滚风险。

怎么快速定位持有MDL锁的Sleep连接
真正卡住DDL或DML的,往往不是正在执行的语句,而是那些Command = 'Sleep'、Info为空、但Time值高达几百甚至几千秒的连接。这类连接通常对应一个已BEGIN但未COMMIT/ROLLBACK的事务,MDL锁从BEGIN那一刻起就已持有,直到事务结束才释放。
直接执行:SHOW FULL PROCESSLIST;
重点关注三列组合:Command为Sleep、State为NULL或空、Time > 300(秒)。这类线程ID就是首要怀疑对象。
补充验证方式(避免误杀):
- 查information_schema.INNODB_TRX,确认该线程ID是否出现在trx_mysql_thread_id中,且trx_state = 'RUNNING'
- 查performance_schema.metadata_locks(MySQL 8.0默认开启;5.7需手动启用),过滤OWNER_THREAD_ID匹配该线程ID,看是否持有SHARED_WRITE或EXCLUSIVE锁
- 若mysql tables in use和locked tables均为0(通过SHOW ENGINE INNODB STATUS\G的TRANSACTIONS部分查),基本可判定无实际数据操作,仅持锁空转
为什么不能直接KILL,而要先KILL QUERY
对一个挂起的事务连接执行KILL <thread_id>会强制断开连接并回滚整个事务,看似干脆,但有风险:
- 如果该连接正被应用连接池复用,突然断连可能触发连接池异常重连逻辑,造成短暂抖动
- 若事务已写入大量undo日志,回滚本身可能耗时数分钟,期间仍阻塞其他DDL
- 某些ORM框架(如Hibernate)在事务异常中断后可能残留脏状态,影响后续请求
更稳妥的做法是分两步:KILL QUERY <thread_id> → 中断当前语句(对Sleep连接效果等同于无操作,但安全)
观察10–20秒,若State仍未变,再执行KILL <thread_id>
注意:KILL QUERY对纯Sleep连接无效,但它是个“试探动作”——若连接真在执行长查询,这一步就能提前释放MDL,避免升级到KILL。
MySQL 5.7 vs 8.0 查MDL锁的差异点
MySQL版本直接影响你能看到什么信息,别用错SQL:
MySQL 5.7:
- performance_schema.metadata_locks表默认关闭,需先执行:UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'global_instrumentation';UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
- 没有sys.innodb_lock_waits视图,依赖手写JOIN:SELECT r.trx_id AS waiting_trx, b.trx_id AS blocking_trx FROM information_schema.innodb_lock_waits w JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
MySQL 8.0:
- performance_schema.metadata_locks默认开启,可直接查:SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED';
- 推荐用sys.schema_table_lock_waits(需确保sys库已安装),一行输出等待方+阻塞方+表名+锁类型
kill之后ALTER TABLE还是卡住?检查这三点
执行KILL后,Waiting for table metadata lock状态没立刻消失,常见原因有:
-
innodb_lock_wait_timeout未生效:这个参数只控制行锁等待超时,对MDL锁无效;真正控制MDL等待的是lock_wait_timeout(默认31536000秒),修改它需SET GLOBAL lock_wait_timeout = 60; - 存在多个持锁连接:一个
KILL只能解决一个源头,务必用SELECT * FROM information_schema.PROCESSLIST WHERE Command = 'Sleep' AND Time > 300;扫全量,别漏掉第二个“睡着的”事务 - DDL本身被其他DDL阻塞:比如两个
ALTER TABLE并发执行,后启动的会等前一个完成;此时SHOW PROCESSLIST里能看到多个alter table语句都卡在Waiting for table metadata lock,需按Time升序逐个处理
最易被忽略的一点:MySQL 8.0中,TRUNCATE TABLE也需获取EXCLUSIVE MDL锁,且不支持ALGORITHM=INPLACE,它的阻塞能力与ALTER TABLE相当——排查时别只盯着ALTER。


















