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 和事务划分上去动刀。
