双主复制必须配对设置auto_increment_offset和auto_increment_increment以避免主键冲突;A设increment=2、offset=1生成奇数ID,B设increment=2、offset=2生成偶数ID,且须写入my.cnf并重启生效。

双主复制下 auto_increment_offset 和 auto_increment_increment 必须配对设
不配对就必然冲突——比如两台主库都生成 ID=1、ID=2,写入对方时直接报错 Duplicate entry '1' for key 'PRIMARY'。这不是概率问题,是设计缺陷。
假设两台 MySQL 实例为 A 和 B,必须让它们生成的自增 ID 严格错开:
- A 设置:
auto_increment_increment = 2,auto_increment_offset = 1→ 生成 1,3,5,7… - B 设置:
auto_increment_increment = 2,auto_increment_offset = 2→ 生成 2,4,6,8…
这两个变量只在 server 启动时读取一次(除非动态 SET,但不推荐),所以必须写进 my.cnf 的 [mysqld] 段,并重启生效。仅执行 SET GLOBAL 不持久,重启后还原,等于白设。
为什么不能只改 auto_increment_increment?
只设 auto_increment_increment = 2 而不设 offset,两台机器默认 offset = 1,结果都是 1,3,5…,冲突照旧。MySQL 不会自动帮你区分主库角色——它只认当前配置值。
常见错误操作:
- 在 A 上执行
SET GLOBAL auto_increment_increment = 2,以为“设了就行” → 重启后失效 - 两台都设
offset = 1,只调increment→ ID 完全重叠 - 用脚本临时改 offset,但没确认表当前
AUTO_INCREMENT值 → 新插入可能跳号或卡住
验证是否生效:连上每台实例,执行 SHOW VARIABLES LIKE 'auto_increment%';,必须看到两个变量值稳定且互补。
双主冲突检测不能只靠自增 ID 错开
自增错开只是避免主键冲突的最基础一层。它完全不管业务逻辑冲突,比如:
- 两边同时 UPDATE 同一行的某个字段(如用户余额),最后谁的写入生效取决于复制延迟和执行顺序
- DELETE + INSERT 同一主键,在不同节点时间差内可能引发数据不一致
MySQL 自身不提供行级写冲突检测或自动合并机制。真要防这类问题,得靠应用层加分布式锁、或引入 GTID + enforce_gtid_consistency = ON 配合严格的写入路由(比如只允许特定业务写指定主库)。
另外注意:innodb_autoinc_lock_mode = 2(交错模式)在双主下更安全,避免语句级自增锁导致的复制延迟放大;但若用 INSERT ... SELECT 或批量插入,仍可能因非确定性行为埋雷。
上线前必须检查的三个点
很多双主翻车不是配置错了,而是漏查了这些隐性依赖:
-
server_id必须全局唯一,否则复制线程无法识别来源,日志里会出现Could not find first log file name in binary log index file - 所有表必须有显式主键 —— 没主键的表在 ROW 格式复制下,UPDATE/DELETE 可能误匹配多行,且
auto_increment机制本身也要求主键存在 - 确保
binlog_format = ROW,STATEMENT 格式下自增变量不记录实际生成值,从库重放时可能用错 offset
自增错开只是双主能跑起来的必要条件,不是充分条件。真正稳得住,得每一层都对得上:配置、表结构、日志格式、应用写法——少一个,过几天就出事。










