跨机房高可用不能仅靠堆从库解决,核心矛盾是网络延迟与数据一致性的不可调和,必须在RPO=0(不丢数据)和RTO之间权衡取舍。

跨机房高可用不是靠“堆从库”就能解决的,核心矛盾是网络延迟与数据一致性的不可调和——你必须在rpo=0(不丢数据)和rto(快速恢复)之间做显式取舍,否则部署完才发现切不动、同步断、应用报错连片。
为什么原生主从复制在跨机房下基本不可用
标准 CHANGE MASTER TO + 异步复制在同城机房尚可,在跨城(比如北京-广州)会频繁触发:Retrieval failed from network、Seconds_Behind_Master 突增到数小时、IO_THREAD 反复重连。根本原因不是配置错,而是 TCP 连接在 50ms+ RTT 下极易被中间设备(防火墙、专线设备)静默中断,而 MySQL 原生复制对这类瞬断毫无恢复韧性。
常见误操作包括:
- 直接启用
slave_compressed_protocol=ON—— 跨机房带宽瓶颈不在传输体积,而在 RTT;压缩反而吃 CPU,加剧复制延迟 - 用
keepalived + VIP做故障转移 —— VIP 切了,但 mysqld 可能卡在 recovery 阶段,应用连上却无法执行任何语句 - 依赖 DNS 切换主库地址 —— 客户端缓存、运营商缓存导致生效时间不可控,最长可达 5 分钟
GTID + 半同步是跨机房复制的底线配置
没有 GTID,断连重连后你得手动算 Exec_Master_Log_Pos,跨机房环境下 binlog position 位点极难对齐;没有半同步,网络抖动时复制自动退化为异步,rpl_semi_sync_master_timeout 设太小(如 1000)会导致主库频繁降级,设太大(如 60000)又拖慢写入响应。
必须确认以下三项已启用:
-
rpl_semi_sync_master_enabled=ON且rpl_semi_sync_slave_enabled=ON -
relay_log_recovery=ON(从库宕机重启后自动重建中继日志,否则复制直接中断) -
gtid_mode=ON+enforce_gtid_consistency=ON+binlog_format=ROW
示例关键参数(my.cnf):
server_id = 101 gtid_mode = ON enforce_gtid_consistency = ON binlog_format = ROW rpl_semi_sync_master_enabled = 1 rpl_semi_sync_slave_enabled = 1 rpl_semi_sync_master_timeout = 10000
延迟从库 + 过滤复制是误操作兜底的唯一现实手段
跨机房链路修复窗口通常只有几分钟,等 DBA 登上去查日志再跳过事务早已来不及。主动设置可控延迟才是工程上最可靠的“后悔药”。
执行 CHANGE MASTER TO MASTER_DELAY = 3600 后,该从库永远比主库慢 1 小时——这不是性能缺陷,是安全策略。配合 replicate_do_db 过滤掉非核心库(如 test、audit_log),能显著降低复制压力和磁盘占用。
注意两个硬限制:
-
MASTER_DELAY只对 SQL thread 生效,IO thread 仍实时拉日志,所以Relay_Log_Space不代表实际延迟,需查Seconds_Behind_Master - 延迟从库不能加入读负载均衡池——它连
SELECT NOW()都比主库旧,强行读会引发业务逻辑错乱
ProxySQL 是跨机房高可用的事实入口层
把健康探测、流量路由、故障隔离全交给 ProxySQL,而不是让应用直连 IP 或依赖 DNS。它能持续 ping 后端节点、按权重动态降权、自动剔除 ERROR 2013 (HY000): Lost connection to MySQL server during query 的节点,避免雪崩。
关键配置项:
- 开启
mysql-monitor模块,探测间隔 ≤ 3 秒 - 主库
weight设为 100,延迟从库设为 1(只用于灾备接管,不参与读) - 所有连接强制走
SSL,跨机房链路不加密等于裸奔
真实场景中,90% 的“跨机房切不动”问题,根源不在 MySQL 本身,而在客户端没感知到后端切换 —— ProxySQL 的 mysql_servers 表变更后,应用连接池需支持自动刷新,否则旧连接仍打向已下线节点。
真正难的不是配出能跑的复制链路,而是让整个链路在丢包率 0.5%、RTT 波动 30~200ms 的跨城网络下持续稳定;很多团队卡在“测试环境 OK,一上生产就断”,往往是因为漏掉了 relay_log_recovery 或没关 slave_compressed_protocol —— 这些细节不验证,上线即事故。


















