Skip to content

多活容灾与单元化 ​

定位:工程实践方法论(工程方法论系列收官篇之一)。容灾回答"机房没了怎么办"。多活回答"机房平时也不闲着"。单元化(set 化)回答"异地之间数据不同步怎么办"。三者是同一个问题在三个预算下的解。本篇以金融支付场景为参照(LDC / 三地五中心是单元化的成熟范本)。与 数据迁移与切库方案(双向同步底座)、故障复盘与OnCall体系(演练问题跟踪)、3+1保障(预案体系)互链。

两个度量与容灾形态矩阵 ​

容灾一切选型先算两个数:RPO(能丢多少数据,时间度量)与 RTO(多久能恢复业务)。

容灾形态RPORTO成本关键约束
冷备(备份异地归档)小时~天天级1×备份能不能恢复,全靠演练,多数没验过
同城主备(热备)分钟级(异步复制)十分钟级2×备机平时闲置,切换靠人
同城双活秒级(半同步可到 0)分钟级2×机房 RT 1~3 ms,存储层可跨机房共享
两地三中心分钟级小时级3×异地中心只接管不承载日常流量,接管即降级
异地多活(单元化)≈0(单元内封闭)分钟级N×数据必须能按分片封闭,改造成本最高

矩阵的读法:RPO 需求由业务定(交易/资金类要 ≈0,内容类可容忍分钟级)。RTO 需求由对客承诺定。先定指标再选形态。反过来"先把机房建起来再找指标"是成本灾难。另外冷备一行要加一条脚注:备份的价值等于它的最后一次成功恢复演练。没恢复过的备份视同没有。

text
 同城双活(RT 1~3ms:存储可以跨机房,应用双活)
   机房A(应用+DB主) ◄──半同步复制──► 机房B(应用+DB从)
   两机房同时接流量;主库故障→B 升主,RPO 由复制模式决定

 异地多活(RT 30ms+:一个 DB 没法异地共享写 → 数据必须分片封闭)
   hash(uid) % N ──► 单元1(北京) / 单元2(上海) / 单元3(深圳)
   每个单元:应用+DB 只服务自己分片的读写;某地故障→其分片流量
   切到其他单元(数据先行复制到位),其余分片不受影响

核心判断:同城靠"复制",异地靠"封闭"。RT 决定了异地不可能共享一个存储主库,单元化不是目的,是异地多活被迫付出的代价。

单元化(set 化):流量分片与数据封闭 ​

  • 流量分片:选一个路由因子(uid/商户号/业务线),把全部读写流量按因子归到某个单元(set)。路由因子必须覆盖核心链路全部请求——一个分片内的请求如果散到多个单元,封闭就破了。
  • 数据封闭:一个分片的数据,其写只发生在一个单元(该分片的主)。读尽量也在本单元(近端副本)。封闭的判据:单元内故障或切换,影响面=本单元分片,绝不外溢。
  • 三类 Zone(LDC 的抽象):
Zone放什么读写模式例子
RZone(单元)分片封闭的业务数据与应用本单元读写自己的分片交易、账务、订单
GZone(全局区)无法分片的全局数据全局单写(通常同城双活保它自己)配置、商品池、库存总账
CZone(近端只读)GZone 数据在各单元的只读复制单元内读本地副本,写回 GZone单元内读商品/读配置
  • GZone 是单元化后剩余的集中式单点。它自身用同城双活/强一致复制保高可用,并把"读"下沉到 CZone 消化跨单元读流量。

单元容量与分片设计 ​

  • 分片数是预留位:取单元数的整数倍(通常 2 的幂)。未来加单元 = 搬分片,而不是全量重哈希。路由用"映射表"(分片→单元的可变映射)而非裸取模。这样搬迁才有可能灰度推进。
  • 容量按 (N-1) 校核:任一单元故障,其余单元要承接它迁来的分片流量。N 个单元的常态容量各按 总量/(N-1) 的一档预留。多活的冗余是买定的,不是临时扩容赌出来的。水位用全链路压测实测(见 3+1保障)。
  • 分片搬迁 = 一次小型六步法:目标单元建副本 → 双向同步追平 → 校验 → 改路由表灰度切 → 收同步。动作与 数据迁移与切库方案 完全同构,只是粒度从"库"变成"分片"。
text
 三地五中心(金融异地多活典型拓扑)
   ┌── 城市A ──┐    ┌── 城市B ──┐    ┌── 城市C ──┐
   │ 机房1     │    │ 机房3     │    │ 机房5     │
   │ 机房2     │    │ 机房4     │    │ (仲裁/冷备)│
   │ RZone 1/4 │    │ RZone 2/5 │    │  管控中心  │
   └───────────┘    └───────────┘    └───────────┘
   城市内:两机房双活(存储半同步,秒级 RPO)
   城市间:分片封闭 + 复制预备;任一城市故障 → 其分片流量
   切到对等城市单元;第三城放仲裁与统一管控,防双活互切

单元路由与南北向、东西向流量 ​

text
 南北向流量(用户 → 单元)                     东西向流量(服务间调用)
 ┌────────┐    ┌────────────────────┐
 │ 客户端  │───►│ 接入层 + 单元路由    │      单元内闭环优先:
 └────────┘    │ 按路由因子(uid)定位  │      订单→库存→支付 全在本单元 set 内完成
               │ 所属单元 → 就近接入  │
               └────────────────────┘      跨单元调用 = 例外路径:
  路由层无状态多副本 + 兜底静态路由表           必须监控、限流、逐条审计
  改路由的前提:分片数据已先行复制到位          (一次跨机房 RT×30ms,循环里调用是灾难)
  • 路由与数据的先后序:单元切换/分片搬迁时按四步走。先建双向同步(数据迁移与切库方案 的六步法同构)。数据追平后改路由切流量,最后收掉同步。顺序反了就是"路由已切、数据没到"。
  • 路由层自己是新单点:多副本 + 配置版本化 + 静态兜底。路由层故障时宁可按旧路由运行,也不允许随机散流量。

故障切换的脑裂防护 ​

机房级切换最危险的不是"切不过去",是切过去之后旧的还活着。两边同时接收写,数据分叉,事后无法合流。

text
 事故时序:只切流量,不断旧写
   决策中心判定机房A失联 → 流量切到机房B
   机房A内组件"自愈"升主(它只是被分区,没有死)
   → A/B 双主双写 → 数据分叉 → 回合后无法自动合流

 防护三件套:
   ① 仲裁:切换决策集中在有全局视野的管控中心(第三方机房仲裁/多数派),
           不允许各组件在机房级故障时自主 failover
   ② 租约:单元写权限带 lease,过期未续约自动关闭本单元写入口
   ③ fencing:切换第一步先"断旧"(撤 VIP / 禁写 DB 账号 / 拉黑旧单元),
           确认旧单元写已死,再"迎新"引流
  • 组件内脑裂与机房级脑裂同一思想:Redis 哨兵用 quorum 判死 + majority 选主 + min-replicas-to-write 限制旧主写入。详见 高可用与部署选型-主从哨兵与集群 的脑裂防护一节。三要素完全对应:判定权与执行权分离、多数派仲裁、旧主降权。
  • 组件自主 failover 在机房级故障时反而添乱:从库们各自升主,事后回收成本极高。成熟做法是把"机房级切换权"上收到统一决策中心。组件只保留节点级自动切换,机房级动作一律编排执行。

金融场景的机房级演练 ​

预案的价值只有在演练中才能验证(没被演练验证过的预案等于不存在):

  • 分级演练分三级。组件级:杀节点/杀从库,高频自动化。机房级:低峰期真实切断一个机房的接入/网络,季度一次。地域级:极端场景,年度或大促前专项。
  • 演练也是变更:有时间窗(低峰)、有回切步骤、有资损预案。金融场景先通过对账系统确认演练窗口内无资损。对账不平立即回切。
  • 机房级演练三段检查单:
    • 切换前:对账基线快照、回切脚本预演、客服公告模板备好。值守表(指挥/操作/记录三角色),分工同 故障复盘与OnCall体系 的作战室。
    • 切换中:先 fencing 确认旧单元写已死 → 分批引流 10%→50%→100%,每批看错误率/延迟/对账三指标。
    • 切换后:全量对账、实测 RTO/RPO 记录入档、问题全部立复盘项。
  • 演练度量的是 RTO/RPO 的实测值:从切断到业务恢复的分钟数(实测 RTO)、切后对账差额(实测 RPO)。矩阵里的承诺值只有演练能兑现。
  • 演练要练"回切":切过去只是半个动作。把流量切回原机房(数据反向追平)同样要演练。只验证单程的容灾能力,第一次真实回切就是事故。
  • 问题验收:演练暴露的所有问题全部进复盘跟踪表,按闭环率验收。例如切换脚本卡住、CZone 脏读、路由层兜底失效。流程见 故障复盘与OnCall体系。演练前的容量水位用全链路压测摸底,见 3+1保障。

追问链 ​

面试官:为什么同城双活不用单元化,异地多活必须?

决定因素是机房 RT。同城 1~3 ms,事务与半同步复制跨机房可用,存储层能共享,双活靠"复制"就能成立。异地 30 ms+,一次事务跨机房往返直接把 P99 打穿。一个主库没法异地服务写,只能把数据按分片封闭进单元,写不过广域网。单元化是被迫的,不是先进的。没有异地多活诉求就上单元化,是为碰不到的故障付常年复杂度。

面试官:单元路由中心挂了怎么办?

路由层按无状态多副本部署(多地多活),路由表配置版本化下发。全挂时客户端/接入层按最后一次下发的静态路由继续运行(宁旧勿乱)。同时它自身是核心 GZone 服务,按最高等级容灾。真正的红线是:路由层任何故障场景下都不允许"随机分配单元"。那等于把封闭打碎,任何单元都可能收到不属于自己分片的写。

面试官:跨单元调用怎么管?

原则"单元内闭环优先":按路由因子分片后,核心链路设计上应单元内完成。跨单元调用是例外路径,要挂三类约束。监控:跨单元调用量/RT 独立大盘,上涨即告警,防止业务演化悄悄破坏封闭。限流:跨单元通道限流保护广域网带宽。审计:逐条登记调用方与目的,供封闭性巡检。循环里做跨单元调用按事故处理。

面试官:RPO=0 是怎么做到的?

不是复制做到的,是封闭 + 切换顺序做到的。分片数据平时就双向/多地复制在目标单元(同步或强增量)。切换只是改路由不搬数据,所以切换本身零丢失。真正丢数据的窗口只剩"决策延迟":从机房故障到仲裁判定、fencing 生效之间未同步的增量。这靠半同步/强一致复制 + 快速仲裁压到接近零,并在演练中用对账实测验证。

面试官:单元化改造从哪一步开始落地?

不是全局一步到位,是单业务线纵深试点。先选分片因子(覆盖该链路全部请求)。存储按分片重分布(复用 数据迁移与切库方案 六步法)。流量按单元路由,再做跨单元调用治理。最后以机房级演练收尾。

跑通一条线再横向复制。没有演练验收手段的"全局单元化",只是把复杂性先建起来,把收益永远留在 PPT 里。

一句话总结 ​

同城靠复制,异地靠封闭;分片决定流量去哪,封闭决定故障停在哪,仲裁+fencing 决定切换不裂开。 多活的信心不画在架构图上,写在一次次机房级演练的实测 RTO/RPO 报告里。

2026-10 新增(工程方法论系列)。