多个站点在线,只是多活的起点
异地多活通常指多个地理分离的站点同时承接业务。真正需要回答的却不只是“有几个机房”:一次写入由谁负责,数据复制到哪里,网络中断时哪些操作还能继续,一个站点故障后如何接管尚未完成的业务?
下面以一个假设的订单系统为例,沿备份、主备、应用双活和分片多活逐步分析。它们是应对不同故障范围的方案,不是每个系统都必须走完的升级阶梯。架构的目标是把业务允许的中断与数据损失,变成可以执行和验证的恢复过程。
先定义业务能接受什么
可用性可以按成功请求比例衡量,也可以按服务可用时间衡量,必须先固定口径。时间口径下,99.99% 对应每年约 52.56 分钟不可用;换算成每天约 8.64 秒只是平均值,不表示每天都可以发生一次相同长度的故障。Google SRE 可用性对照
常见的稳态近似公式是 Availability = MTBF / (MTBF + MTTR),这里将 MTBF 按平均正常运行时长使用,MTTR 表示平均恢复时长。真实服务还应根据业务请求观测,不能只凭机器存活时间计算。
容灾另外要明确两个目标:
| 目标 | 订单系统中的问题 |
|---|---|
| RTO:恢复时间目标 | 故障后多久可以重新接受和查询订单? |
| RPO:恢复点目标 | 故障时最多允许缺失多长时间的数据? |
| 业务一致性 | 已确认支付的订单能否丢失?未知支付结果如何处理? |
| 恢复容量 | 剩余站点能接住多少正常流量与重试流量? |
规模小不等于可用性不重要。一个用户很少但承担关键结算的系统,可能比大规模内容推荐更需要严格的数据恢复目标。反过来,也不应因为追求更高的“几个 9”,就在没有演练能力时引入跨地域写入。
从备份到双活,每一步增加什么能力
备份处理数据恢复,副本缩短接管准备
单机故障可能导致暂时不可用,并不必然意味着数据全部丢失。但没有独立备份时,磁盘损坏、误删或损坏写入可能无法恢复。数据库备份应使用支持的一致性备份、快照或备份工具,不能在运行中随意复制文件就认为可恢复。
全量备份结合日志归档可以恢复到更近的时间点,因此 RPO 不一定等于全量备份周期。它取决于可用日志、归档延迟和恢复链路。备份存在也不等于备份可用,必须实际恢复验证。
副本则让另一份数据持续追随主库,减少故障后的数据准备时间。异步复制会落后;半同步复制的确认也不等于副本已经执行完事务、可以立即读到最新值。读取副本和提升副本,需要分别检查应用进度与可接受的数据缺口。MySQL 半同步复制说明
复制还可能同步误删和错误写入,因而不能替代具备历史恢复点的备份。
备用站点提前准备应用与依赖
仅复制数据库,故障时仍要准备应用、配置、证书、密钥、网络与入口。提前部署可以减少这部分工作,但“已启动”不等于“随时可以接管”。
切换至少要确认原写入方被隔离、新写入方数据状态可接受、依赖完整、目标容量足够,再转移流量。DNS 缓存、长连接与在途请求不会因为改了一个解析记录立即消失。AWS 对备份恢复、热备及多站点活跃策略的区分,也强调了恢复准备程度与持续演练的关系。AWS 容灾策略
应用双活与数据多写是两件事
两个站点的应用都接流量、数据库仍按主备运行,是有效的应用双活方案。它的写入依赖和故障边界需要明确,不能简单称为“伪双活”。
| 部署方式 | 平时增加的能力 | 故障时仍需解决 |
|---|---|---|
| 同城应用双活、数据库主备 | 两站点共同承载应用请求 | 主库提升、路由收敛、剩余容量 |
| 两地三中心 | 把一部分灾备资源放到另一地域 | 异地数据进度与完整恢复演练 |
| 异地分片多活 | 不同数据分片在不同站点处理写入 | 分片归属迁移、复制缺口与旧写入隔离 |
同城站点可能共享电力、网络或自然灾害风险。选址应核对实际故障域、网络条件及适用要求,不存在一条通用的“距离不到 1000 公里就不算异地”的定义。增加机房也不能直接换算出确定的可用性百分比。
跨地域写入:先减少冲突,再减少远程调用
把所有应用部署到多个城市,而每笔订单仍要多次同步访问同一个中心数据库,会把跨地域延迟放进关键路径。影响取决于实际网络往返、调用依赖和串行次数;并行请求不能简单按接口数量累加。
一种可操作的改进是按订单或用户等稳定业务键划分数据归属。以下示例中,A、B 同时承接不同分片的写入,同一分片在一个时刻只有一个被授权的写入方。这不要求同一条记录能够在任意站点同时修改。
路由可以把用户 ID 的稳定哈希映射到 1024 个逻辑桶,再将桶分配给站点。逻辑桶不是用户数量,扩容时调整部分桶的归属,不要简单改变取模除数而让大量用户同时迁移。
但用户归属不能解决全部问题:卖家与买家可能属于不同站点,一件商品也可能被所有用户购买。需要按数据所有权设计调用边界,而不是假定所有业务都能按用户完全闭环。匿名用户登录后,还应处理购物车等状态的归属转换;设备 ID 只能辅助路由,不能成为可信身份凭据。
路由信息必须可信,并受到写入侧约束
接入层从认证后的身份推导路由,不直接相信外部传入的“目标单元”头。RPC、消息、定时任务和补偿任务都需要正确传递或重新解析归属;只覆盖同步 HTTP 请求会遗漏后台写入。
写入侧还要校验分片归属与版本。迁移期间,旧路由缓存、旧消费者和在途请求可能继续访问原站点,入口改路由不能阻止这些写入。可以使用受存储端校验的 fencing token 或等价机制,使旧写入授权失效;仅在日志中记录版本号没有隔离作用。
复制保证什么,必须独立说明
双向复制并不天然解决冲突。“失败就重试”只解决部分传输问题,还需要:
- 稳定的事件标识与幂等处理,防止重复应用
- 同一业务对象的版本或顺序规则,防止旧值覆盖新值
- 来源标识或等价机制,避免复制环路
- 删除、模式变化和积压的处理方式
- 可核对的复制水位、差异检测与修复流程
各站点可以只保存自己负责的数据和必要副本,也可以保存更多副本以便恢复。是否全量复制由恢复策略、成本和合规范围决定,不是多活的定义要求。若业务确实允许同一对象多地并发修改,则还需要与业务语义匹配的冲突解决协议,不能直接套用单写分片的结论。
全局库存:集中决策或拆分额度
订单可以按用户分片,库存却常常被多个分片共享。这时有两类设计可以选择。
集中库存服务便于维护一个统一额度,但跨地域请求仍可能依赖它,必须为这个依赖单独设计容量和恢复。另一种方式是预先给站点分配销售额度:例如总库存 100 件,A 获得 60 件,B 获得 40 件,各自只能在本地额度内扣减。
这里的约束是各处可售额度加在途调拨额度等账户状态不能凭空增大。调拨不能在 A 扣减超时后直接向 B 增加,因为 A 的结果可能未知。需要带唯一调拨 ID 的持久状态机,先冻结或扣出源额度,再幂等入账,失败时查询或对账。
额度拆分把一部分协调提前到分配与调拨阶段,换取本地售卖路径;代价是站点可能局部售罄,而别处仍有库存。是否允许调拨、是否等待,以及给用户怎样的反馈,属于业务策略。它并不是“库存无法分片”,也不意味着可以忽略总量约束。
从两个站点到多个站点,拓扑不等于恢复能力
如果 N 个站点两两建立复制连接,连接关系为 O(N²)。采用中心转发的星状拓扑,可以把连接关系降到 O(N)。减少的是连接数量,不是把所有数据分发到所有副本所需的总工作量。
| 比较项 | 两两连接 | 中心转发 |
|---|---|---|
| 连接关系 | 随站点数平方增长 | 随站点数线性增长 |
| 转发路径 | 可以直接到目标 | 经中心转发,可能增加一跳 |
| 容量压力 | 分散在各站点链路 | 中心承受汇聚和扇出 |
| 恢复设计 | 各分片副本与路由分别恢复 | 还需恢复中心转发能力 |
星状结构只是一个选择,分层复制、按分片选择副本等方式也可能合适。中心失效时不能任意找一个站点提升:它必须具备所需数据、复制状态、转发容量和一致的权威归属,否则会制造新的数据缺口。
新增站点同样不只是增加路由表项,还包括数据装载、持续追平、容量准备、依赖验证和灰度接流。拓扑图中的一条线,背后是需要持续维护的恢复链路。
故障切换是一次受约束的归属转移
正常情况下,路由指向分片所有者;故障时,切换要把数据状态与写入权限一起迁移。
一个分片的接管过程可以按以下检查点组织:
- 判断影响范围:区分站点失联、局部依赖故障和控制面异常,避免误切扩大影响
- 确定恢复点:检查目标副本的持久化与应用水位,确认缺口符合业务允许的 RPO
- 隔离旧写入:让原站点失去写入授权;网络不可达不等于原站点已经停止处理
- 授权新归属并接流:更新权威版本、验证容量和依赖,逐步转移请求
- 处理未知业务与对账:用订单 ID、支付幂等键等查询状态,防止切换后重复执行
- 恢复冗余并评估回迁:旧站点恢复后先同步数据;回迁是新的迁移过程,不能直接改回路由
如果无法满足数据损失目标或可靠隔离旧写入,就需要暂停部分写操作,而不是为了“多活”名称允许两个站点继续竞争同一笔余额。
演练应包括单站点退出、复制积压、网络分区、旧路由缓存、依赖失联和目标容量不足。除恢复耗时外,还要检查重复订单、漏单、支付未知状态以及对账结果。平时两边都有流量,只能证明日常负载下都在工作,不能证明任意一边具备故障接管能力。
异地多活的价值,是在更大的故障范围内保留可用业务;这个价值来自明确的数据归属、可信路由和被验证的恢复过程。站点数量、同步中间件和部署拓扑都是实现手段,最终仍要回到同一个问题:故障发生后,哪些业务还能正确地完成?