MySQL主从复制本身不产生死锁,但从库回放事务时真实执行SQL,若存在并发写入、并行复制启用或索引缺失,即可触发InnoDB事务级死锁。

MySQL主从复制本身不产生死锁,但从库回放主库事务时,会真实执行SQL——只要从库存在并发写入、并行复制启用、或表结构/索引缺陷,就完全具备InnoDB死锁的全部条件。报错不是“复制系统出错”,而是从库上真实发生了事务级死锁。
为什么从库SQL Thread或Worker Thread会卷入死锁
死锁发生在从库本地,和主库无关。关键诱因是“多个事务以不同顺序竞争同一组资源”:
- 用户连接直连从库执行了
UPDATE或INSERT,而SQL Thread正在回放同表的另一条UPDATE,加锁顺序相反 - 开启
slave_parallel_workers > 0后,多个Worker Thread并发回放不同事务,但它们操作的表没有PRIMARY KEY或UNIQUE KEY,导致InnoDB用隐式row_id定位,锁范围不可控、顺序不一致 -
INSERT ... SELECT类语句在从库执行(尤其innodb_autoinc_lock_mode = 1时),自增锁与行锁交叉等待,形成“持有AUTO-INC锁→等行锁”和“持有行锁→等AUTO-INC锁”的闭环 - 从库缺少必要索引,导致
WHERE条件走全表扫描,行锁升级为表锁,大幅增加冲突概率
如何确认死锁来自复制线程而非普通应用
不能只看Last_SQL_Error含Deadlock found when trying to get lock——很多自增锁争用表现为长时间阻塞却不报该错误。必须交叉验证:
- 执行
SHOW ENGINE INNODB STATUS\G,定位LATEST DETECTED DEADLOCK段,检查参与事务的mysql-thread-id:若其中一个远小于普通连接ID(如4、7),且trx_query是UPDATE ... FROM relaylog类语句,基本可锁定 - 查
SELECT * FROM performance_schema.threads WHERE TYPE = 'BACKGROUND',找PROCESSLIST_INFO含apply_event或handle_slave_worker的线程 - 运行
SELECT * FROM performance_schema.replication_applier_status_by_worker\G,若某WORKER_ID的LAST_ERROR_MESSAGE为空但LAST_APPLIED_TRANSACTION停滞,且对应线程在INNODB STATUS中显示setting auto-inc lock,就是自增锁死锁
哪些配置或操作会显著放大死锁风险
以下不是“可能”,而是已在生产环境高频复现的触发点:
-
innodb_autoinc_lock_mode = 1(默认)+binlog_format = STATEMENT或MIXED:INSERT SELECT强制表级自增锁,极易与并行Worker Thread冲突 - 从库未设
read_only = ON且未设super_read_only = ON:SUPER用户仍可写,监控脚本或DBA误操作都可能成为死锁一端 - 表无主键或唯一索引:RBR模式下从库回放UPDATE时依赖隐式
row_id,主从row_id计数器独立,导致定位失败、锁行为异常,日志常误标为锁等待 -
slave_parallel_type = DATABASE但业务跨库更新频繁:看似按库分发,实际多个Worker Thread仍可能并发修改同一张物理表
真正难处理的不是死锁本身,而是它常伪装成延迟突增、SQL线程卡住或Seconds_Behind_Master归零不动——这时候别急着跳过错误,先抓INNODB STATUS快照,否则现场几秒后就消失了。


















