必须配对设置auto_increment_offset和auto_increment_increment(如a节点设1和2、b节点设2和2),并确保server-id唯一、gtid开启、row格式、log_slave_updates=on、replicate_same_server_id=0,否则必现主键冲突或循环复制。

不能只改配置就认为双向复制通了,必须配对设置 auto_increment_offset 和 auto_increment_increment,否则 INSERT 会立刻报 Duplicate entry '1' for key 'PRIMARY'。
server-id 和 auto-increment 必须错开
两台 MySQL 实例的 server-id 必须不同(比如 A=1、B=2),且不能为 0 或默认值;auto_increment_increment 在两端都设为相同值(推荐 2),auto_increment-offset 则必须错开(A=1、B=2)。这样 A 生成 1,3,5…,B 生成 2,4,6…,避免自增主键冲突。
常见错误:
- 用 Docker 部署时所有容器
server-id都是默认1,导致复制线程拒绝启动 - 已有数据含自增 ID,但没检查存量 ID 是否重叠(如 A 里已有 ID=2 的记录,B 又生成 ID=2)
- 误以为
auto_increment_increment = 2单独生效,忘了配offset—— 这俩必须成对出现
GTID + ROW 格式是硬性前提
MySQL 8.0 双向复制必须启用 GTID 模式,并强制使用 ROW 格式 binlog。STATEMENT 或 MIXED 在双写场景下极易因 NOW()、UUID() 等非确定函数造成数据不一致。
配置项必须显式写入 my.cnf(不要依赖默认值):
gtid_mode = ONenforce_gtid_consistency = ONbinlog-format = ROW-
log_slave_updates = ON(让从库也写 binlog,否则链路断在中间)
改完必须重启 mysqld,并验证:SELECT @@gtid_mode, @@binlog_format; 返回 ON 和 ROW 才算生效。
replicate-same-server-id = 0 是防循环的关键开关
双主意味着每台机器既要读对方的 binlog,又要把自己的变更写进 binlog 再发出去。若不关掉 replicate-same-server-id,某台机器可能把自己刚写的事件又拉回来重放,导致无限循环、磁盘爆满、SQL 线程卡死。
必须在两台机器的配置中都显式写:
replicate-same-server-id = 0-
log_slave_updates = ON(配合上一条才起作用)
别信“默认是 OFF”——某些容器镜像或云数据库模板会覆盖该值,漏配等于埋雷。
CHANGE MASTER TO 必须用 GTID 模式重配
执行 CHANGE MASTER TO 时,必须带 MASTER_AUTO_POSITION = 1,禁止混用 MASTER_LOG_FILE 和 MASTER_LOG_POS。如果之前启过复制又重配,务必先在两端都执行:
STOP SLAVE;RESET SLAVE ALL;
否则残留的 gtid_executed 或 relay log 会导致 START SLAVE 报错,或者同步位置错乱。密码含 !、$ 等特殊字符时,建议直接在 MySQL CLI 中粘贴执行命令,避免 shell 解析截断。
真正难的不是敲完这些命令,而是确认业务表是否允许双写:无条件 UPDATE、未加时间戳或版本号校验的并发更新、支付/库存类敏感操作,都会在双主环境下产生不可逆的数据覆盖。配置只是链路基础,业务逻辑适配才是落地门槛。











