应优先执行KILL QUERY而非KILL CONNECTION,因前者仅中断语句并立即释放MDL锁,后者强制回滚可能延长阻塞;定位需结合performance_schema.metadata_locks与threads表,或直接查sys.schema_table_lock_waits。

别急着 KILL CONNECTION,先确认谁真在 hold 锁——90% 的误杀反而让锁更久。
为什么 KILL QUERY 比 KILL CONNECTION 更安全
DDL 卡在 Waiting for table metadata lock 时,阻塞源常是未提交的隐式事务(比如应用执行完 SELECT * FROM t1 就挂起),它只持有 MDL_SHARED_READ 锁,不改数据也不写 binlog。此时:
-
KILL QUERY thread_id仅中断当前语句,事务仍存在但锁立即释放(因为语句结束触发 MDL 自动降级/释放) -
KILL CONNECTION thread_id会强制回滚整个事务——若该事务已修改大量行,回滚过程本身持续持有MDL_EXCLUSIVE,阻塞时间翻倍 - 对空闲连接(
Command = 'Sleep'且Time > 300)用KILL CONNECTION是合理的,但必须先确认它没在跑大事务
如何精准定位该杀哪个 thread_id
别只看 SHOW PROCESSLIST,它不显示锁级别和持有关系。正确路径是:
- 先查等待方:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING'; - 再反查持锁方:
SELECT PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_DB, PROCESSLIST_INFO, PROCESSLIST_TIME FROM performance_schema.threads WHERE THREAD_ID = ?;(把上一步的OWNER_THREAD_ID填进去) - 重点过滤:
PROCESSLIST_COMMAND = 'Sleep'且PROCESSLIST_TIME > 60的连接;或PROCESSLIST_INFO为空但PROCESSLIST_DB明确指向目标库表 - MySQL 5.7.19+ 可直查:
SELECT * FROM sys.schema_table_lock_waits WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';,结果里BLOCKING_TRX_ID对应的线程就是目标
performance_schema.metadata_locks 查不到锁?这些配置必须开
该表默认可能全关闭,尤其 RDS 或旧部署环境:
- 确认启用:
SELECT @@performance_schema;必须返回1 - 开启采集器:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 开启消费者:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'global_instrumentation' OR NAME LIKE 'thread_instrumentation'; - 注意:修改后新连接才生效,已有连接的锁状态不会 retroactively 补充
真正危险的是“看不见”的失败事务
有些连接卡在 Waiting for table metadata lock,但 INNODB_TRX 和 PROCESSLIST 都干净——极可能是失败语句残留锁:
- 比如
SELECT * FROM t1 WHERE nonexistent_col = 1;报错后事务没显式ROLLBACK,MySQL 仍维持MDL_SHARED_READ - 查证方式:
SELECT THREAD_ID, SQL_TEXT FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%t1%' AND THREAD_ID IN (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_COMMAND = 'Sleep'); - 这种场景必须
KILL CONNECTION,因为KILL QUERY对已报错语句无效
复杂点在于:同一个连接可能既持读锁又跑着长查询,KILL QUERY 放掉读锁但留下慢查询占资源;而 KILL CONNECTION 干净但代价高。实际操作前,一定用 sys.schema_table_lock_waits 看清阻塞链末端到底是空闲连接、失败语句,还是正在回滚的大事务。











