MySQL主从切换本身不产生分布式死锁,但切换窗口期若叠加并发写入、并行复制及跨节点事务协调缺失,可能因SQL Thread与用户事务抢锁、Worker线程间加锁顺序错位或GTID双写导致本地死锁或类死锁现象。

MySQL 主从切换本身不产生分布式死锁,但在切换窗口期叠加并发写入、并行复制、跨节点事务协调缺失时,可能触发跨线程/跨实例的锁等待闭环——这不是传统意义上的“分布式死锁”(如两套独立数据库系统互等),而是由架构误用导致的类死锁现象。
主从切换时 SQL Thread 与用户事务抢锁
从库开启写入(哪怕只读应用临时直连)后,SQL Thread 回放 relay log 的事务和用户连接发起的 DML 就处于同一 InnoDB 实例中。一旦两者操作重叠行且加锁顺序相反,InnoDB 就会判定为本地死锁:
- 常见错误现象:
SHOW SLAVE STATUS显示Slave_SQL_Running: No,Last_SQL_Error含Deadlock found when trying to get lock - 典型场景:切换前未停写,应用在从库执行
UPDATE user SET status = 'migrating' WHERE id IN (1001, 2002),而SQL Thread正在回放一条UPDATE order SET user_id = 1001 WHERE id = 5001—— 若索引路径不同(一个走user.id,一个走order.user_id),加锁顺序就可能错位 - 关键点:
SQL Thread的mysql-thread-id在performance_schema.threads中类型为BACKGROUND,但它的锁行为和普通事务完全一致;它不是“特殊线程”,只是没走连接池
MySQL 8.0+ 并行复制 Worker Thread 间锁冲突
当 slave_parallel_workers > 0 且使用 WRITESET 或 LOGICAL_CLOCK 策略时,多个 Worker Thread 可能并发回放不同事务,但若事务修改了相同索引范围且未严格按 commit_order 协调,就会相互阻塞:
- 容易踩的坑:
WRITESET依赖唯一键哈希分发,但如果事务里含INSERT ... ON DUPLICATE KEY UPDATE且冲突字段是二级唯一索引,InnoDB 会在间隙上加锁,而不同 Worker 对同一间隙的加锁请求可能形成等待链 - 性能影响:这种冲突不会立刻报错,而是表现为
Seconds_Behind_Master突增、Slave_SQL_Running_State卡在waiting for handler commit或executing - 验证方式:查
SHOW ENGINE INNODB STATUS\G的LATEST DETECTED DEADLOCK,看参与事务的trx_mysql_thread_id是否都属于低编号后台线程(比如12、13、14),且trx_query都来自 relay log
应用层双写 + GTID 模式下隐式事务交叉
启用 GTID 的主从切换常伴随应用双写(同时往旧主、新主发请求),若应用未控制事务边界,可能让同一个逻辑业务操作被拆成两个物理事务,在新旧主之间形成跨实例锁等待假象:
- 使用场景:切流灰度期,部分流量仍打向旧主,部分已切到新主;但用户 session 未清理,旧事务残留锁未释放
- 参数差异:
gtid_next设置不当(如手动设为AUTOMATIC后又显式指定)会导致事务在新主上重复执行,而该事务在旧主上还未提交,造成锁状态不一致 - 为什么不算真正分布式死锁:MySQL 实例之间无锁同步机制,所谓“跨实例等待”其实是应用端感知到超时或失败后重试,底层仍是各自实例内的本地死锁或锁等待;只是人眼看起来像“A 等 B、B 等 A”
真正需要警惕的是那些看似切换完成、实则残留事务未清理、又叠加并行回放与应用直连写入的混合状态——这时候锁冲突不是概率问题,而是时间窗口内的必然结果。


















