mysql主从复制中如何避免死锁_并发控制方案

来源:这里教程网 时间:2026-02-28 20:52:51 作者:

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

相关推荐