Seconds_Behind_Master: 0不代表ID安全,因Exec_Master_Log_Pos仅表示binlog位置追平,不反映AUTO_INCREMENT计数器状态;两节点各自按SELECT MAX(id)+1初始化且互不感知,GTID下同样存在冲突风险。

双主架构下写入自增ID冲突不能靠SHOW REPLICA STATUS判断是否“已同步”——它根本不反映自增计数器状态,必须人工校准各节点的AUTO_INCREMENT值和配置组合。
为什么Seconds_Behind_Master: 0不代表ID安全?
MySQL 复制线程追平 binlog 位置(Exec_Master_Log_Pos)只说明日志已读完,不表示本机AUTO_INCREMENT计数器已对齐。两台机器各自按SELECT MAX(id) + 1初始化该值,且完全不感知对方当前最大 ID。GTID 模式下同样如此。
常见现象:
- 切换新主后第一笔插入就报
ERROR 1062 (23000): Duplicate entry 'X' for key 'PRIMARY' - SQL 线程卡在某条
INSERT上,错误日志反复出现相同 ID 冲突 -
SHOW VARIABLES LIKE 'auto_increment%'显示已配 offset/increment,但插入仍撞 ID
必须立刻执行的三步现场排查
别先查日志或跳过错误——那是掩盖问题。直接定位真实 ID 状态:
- 暂停所有写入:确认应用层无双写、无跨库直连(尤其检查定时任务、离线脚本)
- 查所有节点当前最大 ID:
SELECT MAX(id) FROM tbl_name;—— 注意是“所有节点”,不是仅新主库 - 比对
SHOW CREATE TABLE tbl_name输出中的AUTO_INCREMENT值,看是否 ≥ 所有节点MAX(id)+ 1
若不满足,立即执行:ALTER TABLE tbl_name AUTO_INCREMENT = N;(N 为所有节点MAX(id)最大值 + 1)
auto_increment_offset和auto_increment_increment配对失效的典型场景
这两个参数必须同时设、写进my.cnf并重启mysqld才生效。运行时SET GLOBAL无效,复制线程仍用启动时加载的旧值。
容易踩的坑:
- 只改了
auto_increment_offset = 2,忘了配auto_increment_increment = 2→ 两台仍都生成 1,2,3… - 配了
increment = 2,但offset = 3(超出范围)→ MySQL 启动时静默重置offset为 1 - 配置写了但没重启:
systemctl restart mysqld,只reload或 kill -HUP 不起作用 - 漏设
innodb_autoinc_lock_mode = 0→ 并发INSERT ... SELECT时跳号、错号,导致后续插入撞 ID
UPDATE/DELETE 冲突根本没法靠自增参数防住
auto_increment_offset和auto_increment_increment只解决INSERT主键重复,对以下操作完全无效:
-
UPDATE accounts SET balance = balance + 100 WHERE id = 123:两端并发执行,最终只加一次 DELETE FROM logs WHERE created_at :因复制延迟或跨时区,两边删的行集可能完全不同- 无条件
UPDATE t SET status='done':延迟期间全表误更新
这类冲突不会报Duplicate entry,而是静默覆盖数据——你得靠应用层幂等设计(如带WHERE version = ?)、逻辑删除、或彻底禁用双写路径来规避。
最常被忽略的一点:以为配好自增参数就高枕无忧,结果UPDATE覆盖在生产环境持续发生两周才被业务侧发现。


















