双向同步必须双方开启log_slave_updates,否则循环链路断开;开启后须配合gtid或server-id过滤防循环,仅错开auto_increment无法避免业务层冲突。

双向复制必须开 log_slave_updates,否则链路直接断;但开了之后不加过滤或 GTID,INSERT/UPDATE 会无限循环。
log_slave_updates 是双向同步的硬性开关,不能只在一边开
MySQL 不区分“主”或“从”角色,只认 server-id 和日志流向。双向同步本质是 A←→B 互为源和目标,所以双方都必须写 binlog 才能回传变更。
-
log_slave_updates=ON必须写进my.cnf并重启生效(MySQL 8.0.22+ 支持SET PERSIST,但生产仍建议配置文件固化) - 运行时
SET GLOBAL log_slave_updates = ON在多数 8.0 版本中无效,别试 - 不开它,B 同步完 A 的变更后不会记入自己的 binlog,A 就收不到 B 的回传——循环链在第一步就断了
- 常见误操作:以为“我当前是主库,不用开”,其实双向场景下没有纯主库
防循环必须靠 GTID 或 server-id 过滤,别信 replicate_ignore_db
replicate_ignore_db 看似简单,实际在跨库语句、存储过程、USE 切换时完全失效,属于高危幻觉配置。
- 推荐方案:启用
gtid_mode=ON+enforce_gtid_consistency=ON,MySQL 会自动跳过已执行过的 GTID(基于gtid_executed集合去重) - 若不用 GTID,必须手动配
replicate_ignore_server_ids:A 的server-id=1,就在 A 上加replicate_ignore_server_ids=2;B 对称配置replicate_ignore_server_ids=1 -
replicate_do_table或replicate_wild_do_table可做细粒度控制,但维护成本高,仅适合极少数表参与双向写
auto_increment_offset/increment 只防主键冲突,不防业务冲突
错开自增 ID 只解决 INSERT INTO t(id) VALUES () 的重复主键问题,对业务层并发写同一行、唯一键冲突、DELETE+UPDATE 顺序敏感等毫无作用。
- A 和 B 同时执行
UPDATE users SET status='paid' WHERE order_id='ORD-001',结果取决于谁先执行,不是合并而是覆盖 - 同时插入
email='a@b.com'(带唯一索引),照样报ERROR 1062 (23000): Duplicate entry - 真正防冲突得靠应用层写路由:用一致性哈希把同一
user_id固定打到同一节点,MySQL 本身不提供该能力
GTID 不解决冲突,只让冲突暴露得更快更准
GTID 让每个事务带唯一 ID,但它不比较语义、不仲裁先后、不阻止执行——两个 UPDATE 同一行的事务各自成功提交,各自生成不同 source_uuid 的 GTID,对方照单全收并执行。
- 冲突发生时,SQL 线程会直接中断,报错如
Could not execute Update_rows_v1 event on table test.t; Duplicate entry '5' for key 'PRIMARY' - 有 GTID 时你能快速定位是哪个 GTID 导致失败,没 GTID 你得手动比对 binlog 位置,排查成本翻倍
- 不要指望
gtid_mode=ON自动帮你“选优”或“合并”,它只是个身份证,不是裁判
最易被忽略的一点:双向复制不是 MySQL 内置功能,而是靠参数组合模拟出来的架构。所有“防循环”“防冲突”机制都是补丁式设计,稳定性远低于单向复制或组复制(Group Replication)。真要高可用,优先考虑 MGR 单主模式,而不是在双向复制上反复调参。











