从库SQL线程卡在Waiting for table metadata lock是因主库DDL引发的MDL锁争用,需定位并温和终止持锁的业务连接而非KILL SQL线程,主库应优先采用pt-online-schema-change、INSTANT算法及合理超时配置预防。

从库SQL线程卡在 Waiting for table metadata lock,不是复制延迟本身慢,而是被主库发来的DDL持有的MDL锁堵死了——必须先定位持锁源,再温和释放,不能直接KILL SQL线程。
查从库是否真被MDL卡住
别只看 Seconds_Behind_Master,它可能为0但SQL线程已挂起。重点看:
-
SHOW PROCESSLIST中 system user 线程的State字段是不是Waiting for table metadata lock -
SHOW SLAVE STATUS\G里Slave_SQL_Running_State是否卡在这个状态,且Retrieved_Gtid_Set和Executed_Gtid_Set差一个GTID(对应主库刚执行的ALTER TABLE) - 执行
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED' AND OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table',确认是否有长时持有者
定位真正持锁的连接(不是SQL线程)
持锁者往往是个“睡着的”业务连接:Command = 'Sleep'、Time 很大(比如 > 60)、但在 INNODB_TRX 里 trx_state = 'RUNNING' 且 trx_query IS NULL。这是事务没提交导致MDL一直挂着。
- 查
information_schema.INNODB_TRX,按trx_started倒序,筛:trx_autocommit = 0、trx_state = 'RUNNING'、trx_query IS NULL - 用查到的
trx_mysql_thread_id去performance_schema.threads找对应THREAD_ID - 再查
performance_schema.metadata_locks,确认该线程是否对目标表持有LOCK_TYPE = 'SHARED_READ'或'SHARED_WRITE'
KILL要分两步,避免回滚雪崩
直接 KILL CONNECTION 会触发完整回滚,undo量大时可能持续几分钟,期间锁还在。
- 先执行
KILL QUERY thread_id(对Sleep连接虽无效,但能排除它正在跑长查询的可能) - 等10–20秒,再查
INNODB_TRX状态是否变化;若仍为RUNNING且trx_query IS NULL,再执行KILL CONNECTION thread_id - 注意:绝不能
KILLsystem user 的SQL线程本身,那是复制通道,杀掉会导致复制中断
主库侧预防比从库抢救更重要
线上已卡住是救火,但多数问题其实在主库就埋下了根。关键控制点:
- DDL操作必须用
pt-online-schema-change或gh-ost,它们不走原生DDL路径,绕过MDL长持有 - MySQL 8.0.12+ 支持
ALGORITHM=INSTANT的列变更(如ADD COLUMN),优先用它 - 主库
wait_timeout必须设为300(5分钟),否则空闲连接容易变成“僵尸事务”长期持锁 - 应用层所有连接池(如HikariCP)的
idleTimeout必须 ≤ 300 秒,且客户端默认autocommit=False时,务必显式commit()或rollback()
真正麻烦的不是锁本身,而是持锁者不报错、不退出、不释放——它安静地躺在那里,像一根卡在齿轮里的细铁丝,直到整个复制链被拖停。


















