确认主从复制卡在Waiting for table metadata lock,需查SHOW PROCESSLIST中system user线程状态、performance_schema.metadata_locks中LOCK_STATUS='PENDING'记录及GTID差值;阻塞源是持有MDL的其他会话(如mysqldump、FLUSH TABLES或长事务),应KILL CONNECTION而非SQL线程;预防须用pt-online-schema-change、禁用手动读锁、设lock_wait_timeout并优先采用ALGORITHM=INSTANT。

主从复制卡在 Waiting for table metadata lock,基本就是从库 SQL 线程被 MDL 阻塞了,不是慢,是堵——DDL 拿着写锁不放,后续所有 DML 回放全得排队。
怎么确认是元数据锁卡住的?
别只看 Seconds_Behind_Master,它可能为 0 或虚高。必须查两个地方:
- 从库执行
SHOW PROCESSLIST,找system user身份的线程,State 是Waiting for table metadata lock - 查锁详情:
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING',重点看OWNER_THREAD_ID是否指向那个 system user 线程 - 再比对 GTID:
SELECT @@global.gtid_executed;在主库查,然后在从库SHOW SLAVE STATUS\G中看Retrieved_Gtid_Set和Executed_Gtid_Set差的那一个 GTID,如果对应的是ALTER TABLE,就坐实了
为什么 kill 掉 SQL 线程没用?
因为阻塞源从来不是 SQL 线程自己,而是别的连接占着 MDL 不放。常见肇事者:
- 未结束的
mysqldump --single-transaction(忘了加--skip-lock-tables) - DBA 手动执行了
FLUSH TABLES WITH READ LOCK后没释放 - 某个长事务还在跑
SELECT ... FOR UPDATE或普通SELECT(MDL 在事务提交前不释放) - 监控脚本或定时任务连上来做了
LOCK TABLES
所以该 KILL CONNECTION {id} 的,是这些持有锁的会话,不是 SQL 线程本身。
如何快速定位并清理阻塞源?
先快速筛出可疑连接:
- 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60 - 查正在等 MDL 的线程:
SELECT t.ID, t.USER, t.HOST, t.COMMAND, t.TIME, t.STATE, t.INFO FROM performance_schema.threads t JOIN performance_schema.metadata_locks ml ON t.THREAD_ID = ml.OWNER_THREAD_ID WHERE ml.LOCK_STATUS = 'PENDING' AND t.PROCESSLIST_COMMAND != 'Sleep' - 查活跃连接里带锁关键词的:
SELECT * FROM information_schema.PROCESSLIST WHERE INFO LIKE '%ALTER%' OR INFO LIKE '%LOCK%'
拿到 ID 后直接 KILL CONNECTION {id}。注意:MySQL 5.7+ 必须用 KILL CONNECTION,KILL 默认只杀查询,不杀连接。
怎么避免下次再卡?
预防比抢救重要,尤其线上不能靠每次 KILL 救火:
- 主库执行 DDL 前,必须用
pt-online-schema-change或gh-ost,它们不走原生 DDL,不长期持 MDL - 从库禁止任何手动
FLUSH TABLES WITH READ LOCK;备份必须加--single-transaction --skip-lock-tables - 调低
lock_wait_timeout(建议 30–60 秒),让 DDL 失败得快,而不是挂一年 - MySQL 8.0.12+ 的小变更(如加虚拟列、改注释)优先用
ALGORITHM=INSTANT,完全不触发 MDL
真正难处理的不是锁本身,而是那些没人认领的长连接——它们可能来自某个凌晨跑完就断开的脚本,也可能来自某次忘记关掉的客户端。这类连接不会报错,但会静默锁死整个复制链路。


















