
从库回放时为什么会出现锁等待甚至死锁
MySQL 从库在 SQL 线程回放 relay log 时,会以单线程(或 MTS 多线程)方式重放主库的写操作。如果主库上并发执行了多条修改同一行/同一索引范围的语句,从库回放时可能因执行顺序、事务隔离级别、gap lock 行为不一致,导致锁冲突——比如 INSERT ... ON DUPLICATE KEY UPDATE 和 DELETE 在从库上先后执行却卡住。
- 主库写入是并行的,但从库回放是串行(即使启用 MTS,也只在不同 database 或逻辑时钟下才真正并行)
- 从库默认开启
innodb_lock_wait_timeout,但超时后报错是Lock wait timeout exceeded,不是主库原错误 - 从库的
READ COMMITTED和主库不一致时(比如主库用REPEATABLE READ),gap lock 范围不同,容易在回放时触发额外锁
怎么查出正在阻塞的从库回放事务
不能只看 SHOW PROCESSLIST,SQL 线程显示 Waiting for table metadata lock 或 Updating 并不说明问题根源;得结合锁视图定位真实持有者和等待者。
- 执行
SELECT * FROM performance_schema.data_locks(5.7+)或INFORMATION_SCHEMA.INNODB_TRX+INNODB_LOCK_WAITS(5.6) - 重点过滤
trx_mysql_thread_id对应 SQL 线程 ID(SHOW SLAVE STATUS\G中的Sql_thread_id) - 对比
trx_state = 'LOCK WAIT'的事务与trx_state = 'RUNNING'的事务的trx_query和锁类型(lock_mode,lock_type) - 注意:从库上
autocommit=1的语句也会产生事务,别漏掉短事务
如何避免主从复制中因锁引发的延迟或中断
核心思路不是“等锁释放”,而是减少锁竞争面、缩短锁持有时间、让回放行为更可预测。
- 主库写入尽量避免大事务:单个事务超过 10MB relay log 或持续 > 30 秒,从库回放极易被锁卡住
- 批量更新改用
WHERE id IN (x,y,z)替代循环单条UPDATE,减少事务数量和锁粒度 - 从库设置
innodb_flush_log_at_trx_commit=2(仅限非强一致性场景),降低刷盘开销对锁释放的影响 - 禁用
binlog_row_image=MINIMAL(除非确认业务无 UPDATE 涉及二级索引变更),否则从库回放时无法精确判断锁范围
遇到 Deadlock found when trying to get lock 且只在从库出现怎么办
这几乎可以断定是主从执行路径差异导致的——主库没死锁,从库回放时因顺序改变触发了环形等待。它不是配置错误,而是数据+逻辑+并发共同作用的结果。
- 先确认是否真死锁:查
SHOW ENGINE INNODB STATUS\G,看LATEST DETECTED DEADLOCK部分,注意TRANSACTION的mysql_thread_id是否含从库 SQL 线程 ID - 不要盲目调高
innodb_deadlock_detect,它默认已开启;重点看死锁中涉及的表是否有UNIQUE KEY冲突或INSERT ... SELECT类操作 - 临时缓解可用
SET GLOBAL slave_parallel_workers = 0切回单线程回放(牺牲性能换确定性),再针对性优化主库 DML 模式 - 长期方案:把易冲突的业务逻辑拆到单独库表,或加应用层重试 + 延迟补偿,别指望 MySQL 自动解决跨节点的锁序问题
锁冲突在主从环境里从来不是孤立事件,它暴露的是主库写法、从库配置、表结构设计三者的耦合缺陷。越想靠参数绕过,越容易在某个低峰期突然爆发延迟雪崩。











