核心结论

单元化不是“多建几套机房”,也不是把应用复制到多个城市就结束。它解决的是一个更具体的问题:当业务规模大到单机房、单库、单服务集群都无法继续水平扩展时,如何让一部分用户或一部分业务身份的请求,从接入层、服务层、中间件到数据层,都尽量闭环在同一个逻辑单元里。

一个成熟的单元化系统,至少要把四个域对齐:

需要对齐的内容 失配后的结果
流量域 请求能按用户、商家、地域、订单等分片标识进入指定单元 入口正确,内部调用仍然乱跳
数据域 核心数据按同一分片规则落到对应单元 服务闭环,数据库跨城访问
故障域 单元故障只影响该单元承载的流量和数据 一个局部故障拖垮全站
治理域 路由规则、注册中心、发布、监控、演练都理解单元 规则靠人工同步,切换时失控

这个定义能把几个常被混用的词分开:

概念 隔离对象 是否要求存储隔离 常见用途
AZ(Availability Zone) 基础设施故障域 不要求 机房级容灾、容量冗余
Set / Cell 业务流量和业务数据 通常要求 容量扩展、故障隔离、异地多活
RZone / GZone / CZone 数据类型和访问路径 要求分类处理 蚂蚁单元化里的区域模型
LiteSet / 泳道 链路流量 通常不要求 全链路灰度、压测、环境隔离
Shuffle sharding 租户到资源子集的绑定 不一定 多租户爆炸半径控制

单元化的判断标准也很朴素:一笔请求进入系统后,绝大部分调用是否能沿着同一个分片规则走完;跨单元调用是否被显式建模;单元故障时是否能按预案迁移流量和数据主权;恢复过程是否不依赖临时扩容、临时改规则、临时补脚本。

从单点到多活

单点不只是一台机器。单服务、单数据库、单机房、单城市,都可能成为系统可用性的硬边界。

部署节点的升级

扩展模式和镜像模式是多机房架构的两个起点。

扩展模式把服务或数据库拆开放到不同机房里,优点是容量变大,缺点是逻辑上仍像一个被拉长的大机房。请求跨机房调用和跨机房访问数据库会变成常态,延迟和故障都会沿着调用链扩散。

镜像模式在每个机房部署相同能力,每个机房承载一部分流量。它更接近容灾目标,但难点转移到了流量切分、数据一致性、切换顺序和故障演练上。两个机房都有服务,不等于两个机房都能独立完成同一类业务。

单机房多活架构

同城多机房通常先解决容量问题,再解决容灾问题。两个机房共用一个服务注册中心时,应用节点在逻辑上没有差别,一次 RPC 调用可能落到另一个机房;数据库主库只在一边时,数据库访问也会出现跨机房路径。把注册中心拆成两个,可以让应用层调用收敛在本机房,但只要数据主权没有跟着拆,数据库跨机房仍然存在。

两地三中心把一个异地中心作为灾备,能提高灾难恢复能力,却很难直接承载核心在线流量。原因不复杂:异地延迟无法消除,异地备库如果不是强一致在线主库,就不能直接承接低延迟交易链路。传统两地三中心更像“同城热备、异地冷备”,资源利用率和切换平滑度都受限。

单元化从这里往前走一步:把站点切成多个逻辑单元,而不是把一个完整站点简单镜像到异地。每个单元功能完整,但只承载一部分流量和一部分数据。

单元的最小定义

LDC(Logical Data Center)可以理解为逻辑数据中心。它不必等于一个物理 IDC。一个 IDC 里可以有多个 LDC,一个 LDC 也可以跨多个物理资源池组织。

单元是一个可独立处理业务的逻辑集合。它包含一组服务、一组中间件配置、一组路由规则,以及分配给它的那部分数据。服务是全量功能,数据是局部数据,流量按分片规则进入。

单元化的设想1.jpeg

这张图的重点不在服务分层,而在每一层都沿着同一个业务身份收敛。接入层识别分片标识,服务层按同样的标识调用,数据访问层按同样的标识落库。只要其中一层不理解分片规则,单元就会被打穿。

单元化一般要求底层数据已经具备逻辑分片能力。一个常见做法是先把逻辑分片一步到位,再逐步调整物理库表。例如按用户 ID 后两位拆成 100 个逻辑分片,初期可以放在少量物理库里,后续再由数据库中间件把这些逻辑分片迁到更多物理库。这样做的好处是业务层面先稳定分片语义,物理容量可以慢慢扩。

这个模式的提炼是:逻辑分片先稳定,物理拓扑后演进。业务代码依赖的是逻辑分片号,不依赖当前到底有几台机器、几个库、几个机房。

蚂蚁单元化的三类区域

公开材料里,支付宝单元化的原始动力主要来自两类压力:核心数据库按用户拆分后,连接数和容量继续增长;异地容灾又要求核心交易链路不能长期依赖远距离访问。公开转载材料提到,支付宝核心数据库曾按用户维度拆成 100 个逻辑分片,单元化改造的一个直接收益是让每个数据库只承载本单元应用节点的连接,避免所有应用节点都连所有数据库。

蚂蚁的单元化资料里,最值得保留的是 RZone、GZone、CZone 这组三分法。它把“数据能不能分片”和“访问频率高不高”拆开了。

区域 数据特征 典型职责 一致性压力
RZone 可以按核心维度分片 订单、支付流水、账户等核心链路数据 高,需要和单元路由强绑定
GZone 不能分片,访问频率较低 全局配置、低频全局服务 可接受较少远程访问
CZone 不能分片,但高频访问 会员、客户信息等公共热数据的本地副本 依赖同步延迟和业务时间差

RZone 最接近理想单元。一个用户、账户或订单所属的 RZone 确定后,核心链路应该在这个 RZone 内完成。GZone 承载不可拆分的全局数据,访问频率低时可以接受一定远程访问。CZone 解决的是更棘手的情况:数据不能按同一维度拆,但每笔核心交易又频繁读取。它通常需要在各地保留本地副本,用同步延迟换低延迟读取。

旧材料里容易出现一个误解:RZone 和 CZone 不是同一类交易流水数据。RZone 承载可分片的核心业务数据;CZone 更像不可分片、高频读取公共数据的本地副本。CZone 能成立,依赖业务上的时间差假设:多数公共数据在被创建之后,不会立刻被异地另一笔交易强依赖。这个假设不是定律,只能通过业务统计、失败兜底和对账补偿来保护。

rcgzone部署.png

这张 R/G/C Zone 的部署图比“多几个机房”的说法更精确。单元化真正关心的是逻辑区域和物理区域的映射关系:某个 RZone 当前在哪个城市、哪个机房承载;GZone 是否只能落在低延迟区域;CZone 的副本如何同步;故障时哪个区域接管哪个区域。

进入两地三中心或三地五中心后,单元和机房不是简单的一一对应关系。一个城市可以承载多个 RZone 的副本,某个 RZone 也可以在多个中心有副本。物理拓扑服务于逻辑单元,而不是反过来让业务流程被机房拓扑牵着走。

两地三中心的一种典型部署.jpeg

三地五中心的典型部署.jpeg

请求如何闭环

单元化请求链路的理想路径是:入口识别分片标识,路由到目标单元;目标单元内的服务调用本单元服务;数据访问层访问本单元数据。跨单元调用不是禁止项,但必须显式出现、显式计量、显式兜底。

一个典型的单元化调用.jpeg

转账是典型例外。一笔交易可能同时涉及付款方和收款方,二者分属不同单元。付款链路可以在付款方 RZone 内闭环,但收款方账户入账可能需要访问另一个 RZone。这个路径不能靠“希望它很少发生”来处理,需要在 RPC 超时、幂等、补偿、对账、消息重放上都做单独设计。

单元化流量管控通常分为五层:

层次 责任
DNS / 客户端规则 尽早把请求送到正确城市或入口
反向代理 根据 Cookie、域名、Header 等轻量标识转发
网关 / Web 层 解析用户态业务参数,写入路由标识
RPC / 注册中心 按单元字段选择服务提供者
数据访问层 作为最后兜底,保证不会访问错数据

单元化流量管控.jpeg

这套分层的意义是多道防线。DNS 不理解业务参数,只能粗粒度调度。反向代理能读到 Cookie,但不适合承载复杂业务判断。网关能解析登录态和请求参数,是业务路由的第一道强防线。RPC 框架和注册中心负责服务调用闭环。数据访问层兜底,保证前面全部失误时也不会把数据写到错误分片。

多层路由必须共享同一份规则。规则中心要描述城市、机房、逻辑单元、分片 ID 到单元的映射关系,还要把规则下发给接入层、网关、RPC 框架、注册中心、数据访问中间件。

统一路由规则.jpeg

服务注册中心需要理解单元字段。服务提供者注册时带上所属逻辑单元,服务消费者根据当前请求的分片标识选择同单元的 provider。没有这个字段,注册中心只能做普通服务发现,无法保证调用闭环。

全局服务注册中心.jpeg

存量系统里,RPC 框架通常不知道哪个参数是分片键。要求所有调用方手工传 ThreadLocal 会污染业务代码,也容易漏。更稳的方式是让服务提供方声明参数提取规则,由框架在调用时按注解或扩展点提取分片标识,再结合全局路由规则选择单元。

注解驱动的rpc.jpeg

这一层改造的本质是把“业务参数里哪个字段决定单元”变成框架可读的契约。单元化不是只改部署脚本,它会反向要求 RPC、注册中心、网关、数据库中间件都具备业务路由意识。

数据层决定切换上限

单元化能不能真正异地多活,最终取决于数据层能不能配合切换。应用层可以很快改路由,数据库主权、复制延迟、脑裂保护、回放顺序、对账补偿才是切换上限。

基于OB的单元化

基于OB的容灾

OceanBase 相关公开资料里,单元化经常和 Paxos、多副本、自动选主一起出现。更现代的 OceanBase Bacchus 论文也延续了同一类数据层思想:数据按 log stream 分区,每个 log stream 有单 leader,日志通过 Paxos 复制,跨分区事务通过两阶段提交协调。具体产品版本和当年支付宝内部实现不能混为一谈,但方向一致:数据库自身要提供复制、选主、故障恢复和一致性边界,不能把这些问题全推给业务层。

OB弹性容量

OB灰度能力

如果底层是传统 MySQL 加 binlog / DRC 复制,也不是不能做 Set 化或单元化,只是工程成本会落到更多外围系统上。常见问题包括:

  • 写入如何标记来源 Set,避免 binlog 回环;
  • 全局 ID 如何携带城市、单元、分片、时间和自增信息;
  • 跨 Set 查询是远程路由,还是复制一份本地只读副本;
  • 同步失败时用 MQ 堆积、RPC 补发,还是人工对账;
  • 切换前是否需要禁写、校验、冻结部分状态型业务。

这些问题不能用“最终一致”四个字糊过去。流水型数据、日志型数据、后置运营流程可以接受补偿;余额、库存、账户状态这类会影响写后写的数据,必须更保守。单元化是在减少跨域一致性路径,不是在消灭一致性问题。

Set 化的工程要点

Set 化和单元化的语义接近,但不同公司会按业务特征给出不同边界。旧手稿图里有很多可回收的工程经验,适合重画成正式架构图。

set化案例1.jpeg

这类 Set 化方案的关键点可以压缩成六条。

第一,入口层必须先于业务服务理解 Set。CDN、Nginx、网关、路由 SDK 都要能识别用户、商家、地域、订单等分片标识。只要存在非 Set 化上游,进入下游前就需要一个统一路由层。

第二,存储层要有 Set 标识。它可以是显式列,也可以编码进 ID。显式列便于查询和校验,ID 编码便于流量染色和路由,但后续迁移、扩容、跨 Set 合并都会更复杂。

第三,中心服务是特殊 Set。元数据、配置、全局账户、结算、红包等服务如果放在中心 Set,要评估 fallback 风险。所有丢失 Set 标识的流量都打到中心 Set,中心 Set 可能先于业务 Set 被打爆。

第四,跨 Set 订单要有固定协议。常见做法是先写远端 Set,再同步回本 Set;MQ 失败时用 RPC 补发;最终再靠版本、幂等键和对账修正。这个协议必须服务化,不能散落在每个业务分支里。

第五,数据冗余程度决定可切换性。全量冗余成本高,但切换快;局部冗余成本低,但跨 Set 查询和容灾切换更难。冗余几份、分片几份、哪些只读、哪些可写,要作为容量模型和故障模型一起计算。

第六,分片属性要足够强。用户维度适合强用户中心链路,地域维度适合 LBS 业务,商家维度适合商家闭环链路。订单同时绑定买家、卖家、门店、连锁品牌时,单一分片键会碰到边界。分片键选错,Set 化收益会被跨 Set 调用抵消。

LiteSet 和泳道不是完整单元化

LiteSet、泳道、全链路灰度常被放进同一个话题,因为它们也在做“流量染色”。但它们的目标是把一类流量沿着链路送进隔离环境,不是异地多活。

LiteSet示意图

LiteSet 通常依赖 Tracer、Header 或上下文标识在中间件之间传递。它可以用于全链路灰度、压测、按业务线隔离部署,也可以在异常时 fallback 回中心链路。它通常不要求存储层分片和数据主权切换,所以不能直接等价为单元化。

这一区分很重要。只隔离流量,不隔离数据,适合灰度;流量和数据一起隔离,才可能承载容灾和容量扩展。一个系统可以先从 LiteSet 获得链路治理能力,再逐步走向真正 Set 化,但不能把 LiteSet 当成异地多活能力。

Amazon 的 Set 化实际是什么

海外材料里很少把“Set 化”作为一个统一术语使用。Amazon 更常见的是三套互相配合的模式:Dynamo 的 replica set、Route 53 这类服务里的 shuffle sharding,以及 AWS 基础设施里的 AZ / static stability。它们和国内单元化相似的地方是控制爆炸半径,不同的地方是隔离对象更偏 key、租户和资源子集,而不是一条完整业务链路。

Dynamo:一个 key 对应一组副本

Amazon Dynamo 论文里的核心路径是:对 key 做一致性哈希,得到 preference list,把对象复制到 N 个节点。读写通过 R、W 参数控制,协调节点向 N 个健康节点发起读写请求。节点故障时使用 sloppy quorum,把副本临时写到其他健康节点;故障恢复后通过 hinted handoff 送回原节点;长期不一致再用 Merkle tree 做反熵。

这套机制里的 set 是“某个 key 的副本集合”。它不是一个完整业务单元,也不要求某个用户的所有业务服务都落在同一个城市。但它非常清楚地回答了一个问题:每个 key 的写入、读取、故障恢复、冲突解决,都被限制在一组可计算的副本集合内。

Dynamo 还把冲突解决留给服务侧。购物车这类业务可以合并多个版本,其他业务可以采用时间戳或业务规则。这个设计和国内强交易链路的单元化差异很大:Amazon 的很多服务更愿意把一致性策略做成 per-service 决策,而不是让一个全局事务模型覆盖所有业务。

Shuffle sharding:每个租户绑定一小组资源

AWS Builders Library 里的 shuffle sharding 更接近很多人说的“set 化”。普通分片把客户分到某一个 shard,某个 shard 过载会影响这一整片客户。Shuffle sharding 则给每个客户分配一个小的资源组合,两个客户即使共享部分资源,也很难完全共享同一组资源。

Route 53 的例子很典型:系统有 2048 个虚拟 name server,每个域名分配 4 个虚拟 name server。组合数极大,足以让每个域名拥有近似独立的虚拟分片。某个域名遭遇 DDoS 时,主要打到它绑定的那 4 个虚拟 name server,其他域名最多共享其中一部分资源,仍有机会通过重试访问到健康资源。

这个模式的公式可以写成:

1
tenant -> choose k resources from N resources -> isolate blast radius by combination

听到“多租户,一个租户的异常不能拖垮同池其他租户”时,shuffle sharding 往往比普通 hash sharding 更合适。它牺牲的是资源调度简单性,换来的是故障相关性下降。

Static stability:故障前就准备好容量

AWS 的 static stability 思想也很适合补进单元化文章。它的核心要求是:不要等一个 AZ 或一个区域坏了以后,再临时扩容、临时搬流量、临时改控制面配置。系统在正常状态下就要预留足够容量,能够在失去一个故障域时继续服务。

这和单元化容灾高度相关。一个城市故障后,如果还要依赖控制面创建资源、改规则、扩容数据库、重建缓存,恢复路径就会被控制面可用性拖住。真正可演练的容灾,应该把接管单元、数据副本、路由规则、缓存预热、限流策略都提前放好。故障发生时,数据面按既定规则运行,控制面只做已经验证过的切换动作。

国内单元化和 Amazon 模式的差异

国内大型交易系统里的单元化,通常从业务身份出发。用户、账户、订单、地域、商家这类业务维度决定流量和数据的位置。目标是让一条交易链路在同一个 RZone 或 Set 内尽量闭环,支撑容量扩展和城市级容灾。

Amazon 的公开材料更强调资源集合和爆炸半径。Dynamo 把一个 key 的副本集合固定下来;shuffle sharding 把一个租户绑定到一小组资源;AZ 和 static stability 把基础设施故障域和容量冗余提前设计好。它不一定要求“一个用户的一整条业务链路”都在同一个业务单元里闭环。

两者可以互相补充:

维度 国内单元化 Amazon 公开实践
起点 交易链路、核心数据库、异地多活 多租户隔离、key-value 存储、基础设施容灾
分片对象 用户、账户、订单、地域、商家 key、domain、tenant、resource subset
主要收益 业务闭环、容量扩展、城市级容灾 爆炸半径控制、静态稳定、服务自治
主要难点 存量系统改造、跨单元交易、数据切主 组合分配、冲突解决、预置冗余成本

一个交易系统做单元化时,可以采用国内路径做业务闭环,再吸收 Amazon 的两个原则:用 shuffle sharding 思路降低租户之间的故障相关性,用 static stability 原则约束容灾能力,不把恢复建立在临时控制面操作上。

一致性边界

单元化不会让 CAP 消失。它做的是把大部分请求变成本单元内请求,让一致性问题局部化。只要出现跨单元写、异地复制、全局状态更新,就仍然要在一致性、可用性和延迟之间做取舍。

更实用的分类不从 CP、AP、CA 开始,而从数据对业务的破坏半径开始。

数据类型 例子 可接受策略
状态型强约束数据 余额、库存、账户状态 强一致、禁写切换、事务或主权明确
流水型可补偿数据 订单流水、支付流水、日志 幂等、最终一致、对账补偿
公共低频数据 配置、规则、字典 中心化或低频远程访问
公共高频数据 会员信息、客户资料 本地副本、同步延迟监控、读失败兜底

状态型数据最怕脑裂多写。流水型数据最怕丢消息和重复消息。公共高频数据最怕副本延迟导致业务误判。不同类型的数据应该进入不同的 zone、不同的复制策略、不同的演练剧本。

RTO 和 RPO 也要按业务链路定义。多站点 active-active 的恢复速度最快,但实现复杂度最高,需要跨区域数据同步和冲突处理。Warm standby、pilot light、backup and restore 都是合法策略,只是恢复时间和资源成本不同。单元化不必把所有业务都推到最高等级,关键是把业务等级和数据策略对齐。

容灾切换顺序

单元故障切换不是从入口流量开始,而是从有状态层开始。数据主权、缓存、消息、注册中心、服务实例、网关规则、DNS 解析,任何一层没准备好,入口切流都会把故障放大。

常见顺序是:

  1. 数据库主权或副本状态确认;
  2. 缓存和本地副本预热;
  3. 消息队列、任务调度、延迟队列切换;
  4. 服务注册中心和 RPC 路由规则切换;
  5. 网关、反向代理、负载均衡切流;
  6. DNS 或客户端规则调整;
  7. 对账、补偿、回放、流量恢复。

一个容易被忽略的故障模式是“只切下游服务”。如果上游仍在原单元,下游被切到另一个单元,上游到下游的调用会变成跨单元路径。很多超时配置按同单元低延迟设计,跨单元后可能大量超时。此时更合适的动作往往是从网关层整单元切流,而不是只把某个服务降级到异地。

容灾演练要覆盖四类场景:单单元不可用、单城市不可用、跨单元网络分区、数据同步延迟扩大。演练不是验证脚本能不能跑,而是验证切换过程中哪些业务必须停写、哪些业务可以有损运行、哪些补偿工具能在多长时间内把账对平。

改造清单

单元化改造可以按这张清单推进。

项目 关键问题 验收信号
分片维度 用户、商家、地域、订单哪个最能闭环 80% 以上核心调用留在同单元
分片标识 请求、消息、SQL、日志里如何携带 任意链路可追踪到单元
路由规则 城市、机房、单元、分片如何映射 多层组件读同一份规则
RPC 改造 如何从参数提取分片键 跨单元调用被显式记录
MQ 改造 消息如何跨单元复制和消费 重放、去重、补发可演练
存储改造 表是否加 Set 列,ID 是否编码单元 数据能按单元校验和迁移
全局数据 哪些进 GZone,哪些进 CZone 高频读不走远程强依赖
容灾策略 哪些业务可有损,哪些必须禁写 RTO/RPO 有分级指标
观测体系 监控是否按单元聚合 单元级容量、错误、延迟可见
演练机制 是否定期做真实切流 预案不依赖临时人工判断

最关键的前置判断是分片维度。单元化不是通用魔法。强用户中心业务适合按用户;强 LBS 业务适合按地域;强商家经营链路适合按商家;双边交易可能需要主维度加副本索引。一个业务如果天然没有稳定分片属性,单元化会变成到处打补丁的跨单元路由工程。

模式速查表

需求关键词 对应模式 方案
单机房容量顶住了 逻辑分片先行 先稳定分片键,再调整物理库表
核心链路跨城慢 流量和数据同域 接入、RPC、DAL 共用单元规则
公共数据高频读 本地副本 CZone 类方案,加同步延迟监控
跨单元交易 显式边界 幂等、补偿、对账、超时单独配置
多租户互相影响 Shuffle sharding 租户绑定一小组资源组合
容灾依赖临时扩容 Static stability 故障前预置容量和规则
灰度链路隔离 LiteSet / 泳道 流量染色,不宣称存储隔离
Set 数据回环 来源标记 binlog 注释、insert_set、update_set

单元化最后拼的是工程一致性。分片键、路由规则、注册中心、消息、数据库、监控、演练,只要有一环不理解单元,单元化就会退化成一组漂亮的部署图。图能说明目标,切换演练才能说明系统真的具备这个能力。

参考资料

  1. 蚁金服支付宝系统的单元化,公开转载材料,适合作为蚂蚁 RZone / GZone / CZone 思路的线索。
  2. Amazon’s DynamoDynamo: Amazon’s Highly Available Key-value Store
  3. AWS Builders Library: Workload isolation using shuffle-sharding
  4. AWS Builders Library: Static stability using Availability Zones
  5. AWS Well-Architected Reliability Pillar: Use defined recovery strategies
  6. OceanBase Bacchus: a High-Performance and Scalable Cloud-Native Shared Storage Architecture for Multi-Cloud
  7. 蚂蚁金服异地多活的微服务体系支付宝如何实现全分布式单元化技术架构?,均为二次传播材料,适合辅助理解,不适合作为唯一事实来源。