server-id重复必然导致循环复制,因MySQL通过server-id硬性过滤同源binlog事件:I/O线程接收时若event的server-id等于本机值则静默丢弃,双主间事务在A→B→A闭环中反复被跳过,造成数据丢失。

为什么server-id重复必然导致循环复制
MySQL 5.7 在非 GTID 模式下,server_id 是唯一用于识别 binlog 事件来源的字段。I/O 线程在写入 relay log 前,会硬性比对 event 中的 server_id 与本机配置值:若相等,直接丢弃,不报错、不记录、不告警。这不是“建议”,而是复制协议底层不可绕过的过滤逻辑。
当 A 和 B 的 server_id 都是 1 时:
- A 写入事务 → binlog 记录
server_id=1→ 发给 B - B 执行后开启
log_slave_updates=ON→ 新增 binlog 仍带server_id=1→ 发回 A - A 收到后比对发现
server_id=1 == @@server_id→ 静默跳过 - 同理,B 也跳过从 A 回传的所有事件 → SQL 线程看似运行,实则数据反复丢失
log-slave-updates 在双主中不是可选项,而是触发点
log_slave_updates=ON 是让从库(即另一主)能把重放的事务写入自己 binlog 的开关。双主架构中,它必须开启——否则对方收不到你的变更。但正因如此,它才把“单向复制”变成“潜在闭环”。没有它,双主形同虚设;开了它,又必须靠 server_id 过滤来斩断闭环。
常见误操作:
- 只在主库开
log_slave_updates,从库关着 → B 不生成 binlog → A 收不到 B 的变更 - 误以为 GTID 开启后可忽略
server_id→ 实测 MySQL 5.7 中即使 GTID 启用,server_id冲突仍会导致 I/O 线程丢事件(GTID 仅防重复执行,不防日志接收) - 在从库上执行
SET GLOBAL log_slave_updates = ON但未写入 my.cnf → 重启后失效,复制链断裂
如何验证 server-id 设置真正生效
修改 my.cnf 后不重启 mysqld,或仅用 SET GLOBAL server_id = N,都无效。I/O 线程只读取配置文件中的初始值,且该值在启动时固化。
确认方式必须三步并行:
- 连接 MySQL 后执行
SELECT @@server_id;,结果必须与my.cnf中server-id的数值完全一致 - 检查所有参与节点(含未来可能加入的从库)的
@@server_id,确保无重复 —— 云环境克隆实例极易继承原server-id,上线前必改 - 避免使用 0 或 1 作为默认值;自动化部署脚本若硬编码
server-id=1,一主多从时必然撞车
最容易被忽略的实战细节
循环复制问题往往在压测或切流后才暴露,因为低流量下丢几个事件不易察觉。真正危险的是:它不报错、不中断复制线程、不写错误日志,只悄悄丢数据。你看到 Slave_IO_Running: Yes 和 Seconds_Behind_Master: 0,但业务侧已出现余额不一致、订单重复扣款。
所以,上线双主前必须做两件事:
- 在测试环境模拟单点写入 + 双向观察:分别在 A 和 B 插入带唯一标识的记录(如
INSERT INTO test_log (node, ts) VALUES ('A', NOW())),再查对方 binlog 是否完整保留server_id来源且无缺失 - 用
mysqlbinlog --base64-output=DECODE-ROWS -v解析双方 binlog,确认事件里server_id字段始终未被篡改,且 A 的事件在 B 的 binlog 中仍标记为 A 的 ID











