mysql主从复制本身不产生死锁,死锁仅发生在主库单机事务并发执行时;从库因单线程(或逻辑多线程)回放relay log且无行锁竞争,不会出现死锁。

MySQL 主从复制本身不产生死锁,但写操作在主库可能引发死锁
死锁只发生在**单机事务并发执行时**,从库是单线程(或逻辑多线程)回放 relay log,不涉及行锁竞争,因此从库不会出现死锁。真正要关注的是:主库上高并发写入时,INSERT、UPDATE、DELETE 语句因锁顺序不一致触发的死锁——它会中断主库事务,导致 binlog 写入不完整,进而让从库 SQL 线程报错卡住(如 Deadlock found when trying to get lock)。
实操建议:
- 主库开启
innodb_deadlock_detect = ON(默认开启),确保死锁能被快速发现并回滚一个事务 - 监控主库状态变量:
SHOW STATUS LIKE 'Innodb_deadlocks';,持续上升说明应用层存在锁序混乱 - 避免在事务中跨多个无关表更新,尤其不要混合操作大表和小表且顺序不固定
- 用
SELECT ... FOR UPDATE时,务必按相同顺序访问表和索引(例如总是先锁users再锁orders)
从库回放阶段的并发控制:如何安全启用 parallel replication
MySQL 5.7+ 支持基于 LOGICAL_CLOCK 的并行复制(slave_parallel_type = LOGICAL_CLOCK),但它依赖事务组提交(group commit)信息。若主库未开启 binlog_group_commit_sync_delay 或 binlog_group_commit_sync_no_delay_count,事务组提交效果弱,从库并行度低,还可能因依赖关系错乱导致数据不一致。
关键配置与避坑点:
- 主库必须设
binlog_format = ROW,STATEMENT 或 MIXED 模式下无法保证事务间逻辑独立性 - 主库开启
binlog_transaction_dependency_tracking = WRITESET(MySQL 8.0.26+),比COMMIT_ORDER更细粒度识别无冲突事务 - 从库设置
slave_parallel_workers = 4~8(不宜超过 CPU 核数),同时开slave_preserve_commit_order = ON防止从库最终一致性被破坏 - 注意
WRITESET依赖primary key或not null unique key,若表无此类键,退化为COMMIT_ORDER,并行效果下降
应用层写入优化:减少主库锁冲突的直接手段
很多“死锁报警”实际源于应用批量写入没控制节奏,比如循环执行 1000 次 INSERT INTO t VALUES (...) 单条语句,每条都启事务、加锁、提交,极大增加锁等待和冲突概率。
更稳的做法:
- 合并写入:用
INSERT INTO t VALUES (...), (...), (...)批量插入,减少事务数量和锁持有时间 - 显式控制事务边界:避免 ORM 自动开启短事务,对关联更新用
BEGIN; UPDATE a; UPDATE b; COMMIT;显式包裹,确保锁顺序可预测 - 读多写少场景下,考虑用
INSERT ... ON DUPLICATE KEY UPDATE替代先SELECT再INSERT/UPDATE,省去一次锁竞争 - 高频更新计数器类字段,改用
INSERT ... SELECT ... FROM DUAL ON DUPLICATE KEY UPDATE cnt = cnt + 1,避免SELECT FOR UPDATE引入长锁
从库延迟与死锁误判:为什么 Seconds_Behind_Master 为 0 还卡住
有时 SHOW SLAVE STATUS 显示 Seconds_Behind_Master: 0,但 Slave_SQL_Running_State 却停在 Waiting for dependent transaction to commit ——这不是死锁,而是 WRITESET 并行复制机制在等上游事务提交,本质是依赖等待(dependency wait),不是锁等待。
排查方向:
- 查
performance_schema.replication_applier_status_by_coordinator,看APPLIED_TRANSACTION和LAST_APPLIED_TRANSACTION是否一致 - 若从库有大事务(如
ALTER TABLE或全表更新),它会阻塞后续所有事务的并行回放,此时应避免在业务高峰期做 DDL -
slave_parallel_type = LOGICAL_CLOCK下,主库单个长事务会导致从库 worker 线程集体空转等待,不如临时切回DATABASE模式(按库分发)保可用性
真正难处理的,是主库事务设计本身没考虑锁粒度和顺序——这类问题不会因为调参数消失,得回到业务 SQL 和事务划分上去动刀。










