必须成对配置auto_increment_increment和auto_increment_offset并写入my.cnf的[mysqld]段且重启生效,A设increment=2/offset=1生成奇数ID,B设increment=2/offset=2生成偶数ID,否则必现主键冲突。

auto_increment_increment 和 auto_increment_offset 必须成对设,且写进 my.cnf
只改 auto_increment_increment 不配 auto_increment_offset,等于没防。两台主库都设 increment=2 但 offset 都是默认的 1,结果两边都生成 1,3,5……一同步就报 Duplicate entry '1' for key 'PRIMARY'。
实操要点:
-
my.cnf的[mysqld]段里配,不是[client]或其他段; - 主库 A:
auto_increment_increment = 2,auto_increment_offset = 1; - 主库 B:
auto_increment_increment = 2,auto_increment_offset = 2; - 改完必须重启
mysqld,SET GLOBAL临时生效不可靠——复制线程可能仍用旧值; - 三节点及以上时,
increment得改成 3,offset分别为 1/2/3,扩容前就得规划好。
为什么 innodb_autoinc_lock_mode = 0 是双主安全底线
默认值 innodb_autoinc_lock_mode = 2(交错模式)在双主里极危险:批量插入如 INSERT INTO t SELECT ... 会跳着分配 ID,导致主库 A 生成 1,4,7,主库 B 生成 2,5,8 —— 看似错开,但一旦复制延迟或重放顺序异常,ID 序列就乱套,Duplicate entry 随时触发。
必须设为传统锁模式:
- 在
my.cnf中加innodb_autoinc_lock_mode = 0; - 该模式下每次 INSERT 都独占自增锁,保证 ID 严格按 offset+increment 递增;
- 性能略低,但双主场景下一致性优先级远高于这点吞吐损失。
INSERT 冲突能防,UPDATE/DELETE 冲突根本防不住
就算 ID 完全错开,UPDATE user SET balance = balance + 100 WHERE id = 123 这类语句在双主上并发执行,结果取决于哪边先同步到对方——A 先加再被 B 覆盖乘,B 先乘再被 A 覆盖加,最终余额不一致,且无法通过 ID 规则规避。
所以真实约束是:
- 禁止应用层向双主同时发 UPDATE/DELETE;
- 所有更新走幂等接口:先
SELECT拿当前值 + 版本号,再带条件UPDATE ... WHERE version = ?; - 删除操作统一转为
UPDATE ... SET is_deleted = 1,避免物理删引发主键复用风险; - 如果必须双写,优先用业务层分片路由(如用户 ID 为奇数写 A,偶数写 B),比 DB 层自增规则更可控。
验证配置是否真生效,别信“我改过了”
改完配置后直接上线?大概率翻车。常见失效原因:配置写错段落、没重启、SQL 线程缓存旧值。必须人工验证:
连上每台 MySQL 执行:
SELECT @@auto_increment_increment, @@auto_increment_offset;
建测试表验证行为:
CREATE TABLE test_inc (id INT PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB;
在主库 A 插入 3 行,查 SELECT id FROM test_inc —— 应得 1,3,5;主库 B 同样操作应得 2,4,6。若出现 1,2,3,说明 offset 根本没生效。
最容易被忽略的是:已有表的 AUTO_INCREMENT 值不会因改参自动调整,但只要新插入不撞已存在 ID,后续分配就不会重——前提是 offset 真的错开了,而不是你以为它错开了。


















