主从复制延迟本质是SQL线程被锁卡住而非执行慢;90%根因为事务锁,典型表现为State: Waiting for table metadata lock,需通过performance_schema.metadata_locks定位持有锁的会话并安全处理。

主从复制延迟不是“慢”,而是从库 SQL 线程被卡在某个事务里不动了——90% 的 case,根源是事务锁,不是网络或磁盘。
查 SHOW PROCESSLIST 时看到 State: Waiting for table metadata lock
这是最典型的锁等待信号。从库 SQL 线程正试图执行一条 DDL(比如 ALTER TABLE),但该表在从库上被另一个长事务或未提交会话占着元数据锁(MDL)。
- 立刻执行
SELECT * FROM performance_schema.threads WHERE PROCESSLIST_STATE = 'Waiting for table metadata lock',确认是哪个线程在等 - 再查
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table',看谁持有锁 - 注意:从库上 不允许手动 kill 持有 MDL 的用户连接(除非你确定它不是业务连接),否则可能破坏复制一致性
- 更安全的做法是先停掉从库复制:
STOP SLAVE SQL_THREAD,再查锁源;处理完再START SLAVE SQL_THREAD
SHOW ENGINE INNODB STATUS 里出现 *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
说明 SQL 线程正在等行级锁,通常是主库发来的一个大 UPDATE 或 DELETE,在从库回放时撞上了本地未提交的事务。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 重点看
TRANSACTIONS部分,找TRX_WAITING_LOCK_ID对应的持有者事务 ID - 检查该事务是否来自应用直连(比如运维脚本、ETL 工具、监控探针),这类连接常忘记
COMMIT - MySQL 5.7 不支持
KILL TRANSACTION,只能KILL对应的thread_id;但务必先确认该线程不是主库 binlog 回放链路的一部分(比如不要误杀 IO 线程) - 如果锁来自一个已死但未清理的事务(
TRX_STATE = RUNNING却无活跃语句),可能是崩溃后残留,需重启从库 MySQL 实例
用 information_schema.INNODB_TRX 定位长事务
从库上存在运行超 60 秒的事务,极大概率会阻塞后续所有 SQL 线程的执行,哪怕它只改了一行。
- 执行
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5 - 特别关注
trx_state = 'RUNNING'且trx_started时间远早于当前时间的记录 - 结合
performance_schema.threads查出该trx_mysql_thread_id对应的PROCESSLIST_USER和PROCESSLIST_HOST,判断是否为非法连接 - 生产环境建议加监控项:
SELECT COUNT(*) FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60
为什么 Seconds_Behind_Master 不涨,但 Slave_SQL_Running_State 卡在 Reading event from the relay log
这不是延迟,是 SQL 线程根本没开始执行——它在 relay log 里读到了一条需要加锁的操作,但锁不可得,于是停在“读取完成、准备执行”的状态。
- 此时
Relay_Log_Space会持续增长,因为 IO 线程还在往 relay log 里写,但 SQL 线程不消费 -
SHOW SLAVE STATUS\G中的Exec_Master_Log_Pos长期不变,而Read_Master_Log_Pos持续前进 - 别信
Seconds_Behind_Master = 0,这个值在锁等待时会失效;真正可靠的指标是Exec_Master_Log_Pos是否推进 - 最直接验证方式:
SELECT MASTER_POS_WAIT('mysql-bin.000001', 123456789, 1)(填你当前Relay_Master_Log_File和Exec_Master_Log_Pos),如果超时返回NULL,说明 SQL 线程确实卡住了
真正难排查的从来不是锁本身,而是那个没人认领的长事务——它可能来自凌晨跑批脚本的遗留连接、DBA 手动调试后忘了 commit、甚至是一个被 kill -9 干掉却没释放资源的 mysqldump 进程。锁住 SQL 线程的,往往不是业务代码,而是运维操作留下的尾巴。

















