应优先终止持有元数据锁(mdl)的源头会话,通过performance_schema.metadata_locks定位lock_duration='transaction'的持锁线程,结合innodb_trx确认长事务,再kill对应processlist_id;避免误杀等待线程,并加强应用连接与事务管理预防。

核心思路是快速定位并终止持有元数据锁(MDL)的源头会话,而不是处理那些已经卡在 Waiting for table metadata lock 的等待线程——杀错对象只会让积压更严重。
直接查出谁在“占着锁不放”
别只依赖 SHOW PROCESSLIST,它看不到锁归属。优先查 performance_schema.metadata_locks:
- 确认 MDL 监控已启用:
SELECT ENABLED FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';
若为NO,执行:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 查当前对目标库表持锁的会话(替换
your_db和your_table):SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST FROM performance_schema.metadata_locks m JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'GRANTED'; - 重点关注
LOCK_DURATION = 'TRANSACTION'的行——这类锁不会随语句结束释放,会一直拖到事务提交或回滚。
锁定长事务,确认是否真“挂起”
持有 TRANSACTION 级 MDL 锁的,大概率是未提交的事务。结合 INNODB_TRX 验证:
- 查活跃事务:
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 10; - 看
trx_started时间戳:比当前时间早几分钟甚至几小时,基本就是罪魁祸首;trx_query可能为空(比如只执行了BEGIN就断开),此时要靠PROCESSLIST_ID关联 threads 表查真实连接状态。
精准 Kill,避免误伤
拿到真正持有锁的 PROCESSLIST_ID 后,执行:KILL [blocking_processlist_id];
- 不要 KILL 等待中的线程(如 ALTER、SELECT 卡住的那些),它们只是受害者;
- KILL 后,该会话事务会回滚,其持有的所有 MDL 锁立即释放,后续积压的 DML/DDL 会按队列顺序自动恢复;
- 如果不确定,可先用
SELECT * FROM sys.schema_table_lock_waits;直接获取blocking_pid,结果更直观。
预防比抢救更重要
线上频繁出现这类问题,说明应用层存在隐患:
- 检查是否有脚本或应用连接池未正确关闭游标和连接(常见于 Python + DBUtils、Java + Druid 场景);
- 禁止在业务连接中执行无意义的长事务,哪怕只是一条
SELECT也要配COMMIT或设为自动提交; - 对 DDL 操作加窗口控制,避开业务高峰,并提前用
SELECT COUNT(*) FROM performance_schema.metadata_locks WHERE ...快速扫一遍目标表是否干净。











