环形复制不能在生产环境用。mysql官方不支持,gtid模式下直接拒绝启动,statement模式下几小时后必出主键冲突、数据覆盖、位点漂移等问题。

环形复制到底能不能用?先说结论
不能在生产环境用。MySQL 官方不支持,GTID 模式下直接拒绝启动,binlog_format=STATEMENT 下看似能跑,但几小时后必出错——主键冲突、数据覆盖、位点漂移全都会来。
为什么 CHANGE MASTER TO ... MASTER_HOST='B' 在节点 A 上会立刻报错?
常见错误现象:ERROR 1794 (HY000): The slave is connected to a different master 或 ERROR 3021 (HY000): This operation cannot be performed with a running slave SQL thread。
这是因为 MySQL 内部用 server_id 做事件过滤和环路检测,一旦发现从库 relay log 里已有自己发出去的 event(靠 server_id + gtid_set 或 binlog position 判断),就会中止 SQL 线程。
使用场景:仅限本地验证链路逻辑,比如测试 mysqlbinlog --read-from-remote-server 是否通;真实业务绝不允许。
实操建议:
- 必须关闭
sql_slave_skip_counter 类绕过手段——跳过的不是数据,是灾难的倒计时
- 检查所有节点的
server_id 是否全局唯一且静态(不能用 SELECT @@server_id 动态查,得看配置文件)
- 确认
log_slave_updates=ON 已开启,否则环路根本建不起来(但开了更危险)
SET GLOBAL sql_log_bin=0 能绕过循环写入吗?
不能。这个设置只影响当前会话产生的 DML/DCL 是否写入本机 binlog,不影响从库线程执行 relay log 后再写入本地 binlog 的行为。
真正起作用的是 replicate-same-server-id=OFF(默认就是 OFF),但它只是让 MySQL 不主动过滤“自己发来的 event”,不等于允许合法循环——后续的 GTID 检查、主键冲突、自增 ID 冲突全还在。
参数差异:
-
replicate-ignore-db 和 replicate-wild-ignore-table 对环路无效,因为过滤发生在 SQL 线程读 relay log 阶段,而循环事件已经混进去了
-
slave_parallel_type=LOGICAL_CLOCK 会让冲突更隐蔽,错误日志里可能只显示 Deadlock found when trying to get lock,找不到源头
如果非得模拟双向同步,有什么替代方案?
用应用层双写 + 最终一致性校验,比数据库层环形复制靠谱得多。
性能与兼容性影响:
- MySQL 8.0+ 的
clone plugin 或 mysqldump --single-transaction 配合应用层幂等接口,延迟可控、可监控
- 避免用
INSERT ... ON DUPLICATE KEY UPDATE 处理冲突——它掩盖了数据不一致,而不是解决它
- 所有跨库写操作必须带业务时间戳或版本号,校验脚本用
SELECT ... FOR UPDATE 锁行比查 SHOW SLAVE STATUS 有用得多
容易被忽略的地方:环形结构下,auto_increment_increment 和 auto_increment_offset 必须在每个节点设不同值,但即便如此,REPLACE INTO 或 INSERT IGNORE 仍会导致主键重复插入失败,而错误被静默吞掉。
CHANGE MASTER TO ... MASTER_HOST='B' 在节点 A 上会立刻报错?
常见错误现象:ERROR 1794 (HY000): The slave is connected to a different master 或 ERROR 3021 (HY000): This operation cannot be performed with a running slave SQL thread。这是因为 MySQL 内部用
server_id 做事件过滤和环路检测,一旦发现从库 relay log 里已有自己发出去的 event(靠 server_id + gtid_set 或 binlog position 判断),就会中止 SQL 线程。使用场景:仅限本地验证链路逻辑,比如测试
mysqlbinlog --read-from-remote-server 是否通;真实业务绝不允许。实操建议:
- 必须关闭
sql_slave_skip_counter类绕过手段——跳过的不是数据,是灾难的倒计时 - 检查所有节点的
server_id是否全局唯一且静态(不能用SELECT @@server_id动态查,得看配置文件) - 确认
log_slave_updates=ON已开启,否则环路根本建不起来(但开了更危险)
SET GLOBAL sql_log_bin=0 能绕过循环写入吗?
不能。这个设置只影响当前会话产生的 DML/DCL 是否写入本机 binlog,不影响从库线程执行 relay log 后再写入本地 binlog 的行为。
真正起作用的是 replicate-same-server-id=OFF(默认就是 OFF),但它只是让 MySQL 不主动过滤“自己发来的 event”,不等于允许合法循环——后续的 GTID 检查、主键冲突、自增 ID 冲突全还在。
参数差异:
-
replicate-ignore-db 和 replicate-wild-ignore-table 对环路无效,因为过滤发生在 SQL 线程读 relay log 阶段,而循环事件已经混进去了
-
slave_parallel_type=LOGICAL_CLOCK 会让冲突更隐蔽,错误日志里可能只显示 Deadlock found when trying to get lock,找不到源头
如果非得模拟双向同步,有什么替代方案?
用应用层双写 + 最终一致性校验,比数据库层环形复制靠谱得多。
性能与兼容性影响:
- MySQL 8.0+ 的
clone plugin 或 mysqldump --single-transaction 配合应用层幂等接口,延迟可控、可监控
- 避免用
INSERT ... ON DUPLICATE KEY UPDATE 处理冲突——它掩盖了数据不一致,而不是解决它
- 所有跨库写操作必须带业务时间戳或版本号,校验脚本用
SELECT ... FOR UPDATE 锁行比查 SHOW SLAVE STATUS 有用得多
容易被忽略的地方:环形结构下,auto_increment_increment 和 auto_increment_offset 必须在每个节点设不同值,但即便如此,REPLACE INTO 或 INSERT IGNORE 仍会导致主键重复插入失败,而错误被静默吞掉。
replicate-ignore-db 和 replicate-wild-ignore-table 对环路无效,因为过滤发生在 SQL 线程读 relay log 阶段,而循环事件已经混进去了slave_parallel_type=LOGICAL_CLOCK 会让冲突更隐蔽,错误日志里可能只显示 Deadlock found when trying to get lock,找不到源头性能与兼容性影响:
- MySQL 8.0+ 的
clone plugin或mysqldump --single-transaction配合应用层幂等接口,延迟可控、可监控 - 避免用
INSERT ... ON DUPLICATE KEY UPDATE处理冲突——它掩盖了数据不一致,而不是解决它 - 所有跨库写操作必须带业务时间戳或版本号,校验脚本用
SELECT ... FOR UPDATE锁行比查SHOW SLAVE STATUS有用得多
auto_increment_increment 和 auto_increment_offset 必须在每个节点设不同值,但即便如此,REPLACE INTO 或 INSERT IGNORE 仍会导致主键重复插入失败,而错误被静默吞掉。











