多区域架构:扛住一个可用区还是整个区域、写落在哪才算数、谁来切流量、能丢多少
比较 Single-region Multi-zone、Active-passive with Warm Standby、Active-active Read Local / Write Global 与 Active-active Write Local 四种区域拓扑:不用人干预能扛住什么故障、一条写要落到哪才算提交、第二个区域会不会落后、谁来切流量、客户端在死掉的区域上停留多久、远方用户读写是本地还是跨越世界、状态归谁,以及一次主区域故障或一份过期的 DNS 答案能波及多远、怎么收住。
一个产品跑在某个地方。那个地方可能丢一个机柜、一栋楼,或者一整个区域。所有区域设计都要回答四个问题:不用人干预能扛住什么故障,是一个可用区还是一个区域;一条写要落到哪才算数,因此第二个区域会不会落后;出事时谁来切流量,客户端会在死掉的地方停留多久;离主区域很远的用户,读和写是在本地还是跨越世界。这四个答案,而不是用的哪家云,才定义了拓扑。
想亲手走一遍四种拓扑、注入故障再恢复,打开互动 Lab:/system-design-lab/multi-region-architectures。
有约束的设计问题
一家公司先后要给四个产品定区域方案:用户集中在一个地区的 SaaS,最怕的是一个数据中心出事,整个区域没了停几小时可以接受,但已确认的数据一条不能丢;受监管的核心业务,区域没了也不能停,RTO 以分钟计、RPO 以秒计,愿意为一个平时不服务的区域付费;全球用户的内容产品,读占绝大多数,写偶尔但同一条记录绝不能出现两个版本;全球用户各自写自己的数据,记录天然按归属区域分开,读写都要本地。每一个该怎么搭?
四种 topology signature
| 架构 | 不用人扛住 | 写落在哪 | 谁切流量 | 远方用户 | 恢复点 / 恢复时间 | State owner | 适合 | 主要代价 |
|---|---|---|---|---|---|---|---|---|
| Single-region Multi-zone | 一个可用区 | 一个区的主库,另一个区的同步备库确认后才算 | 区域级负载均衡和区内的数据库切换 | 每次请求跨越世界 | 零丢失;区内切换 | 主库及其同步备库 | 用户集中在一个区域附近的大多数产品 | 区域没了产品就没了;除了备份没有 DR |
| Active-passive Warm Standby | 一个可用区;一个区域,需切换 | 只在主区域;备区域副本只读且落后 | DNS 按健康检查,或人按开关 | 跨越世界 | 恢复点等于复制延迟;恢复时间是提升加 DNS 传播 | 主区域的主库 | 需要区域丢失预案、可接受有界损失的关键或受监管业务 | 为不服务的区域付费;切换丢最后几秒的写;误报或没围栏会脑裂 |
| Active-active Read Local, Write Global | 一个可用区;读扛一个区域;写要提升 | 唯一主区域,各地转发 | 读按延迟路由;写靠提升加改指 | 读本地,写跨越世界 | 读穿过主区域故障;写停到提升为止;恢复点等于 lag | 全局主库 | 读多写少、写绝不能冲突的全球产品 | 每次写跨区往返;主区域是写的瓶颈和单点 |
| Active-active Write Local | 一个区域,读写都扛 | 本地副本,事后复制给别人 | 地理或延迟路由;没有东西要提升 | 读写都本地 | 没有切换;每区域恢复点等于 lag;冲突的写可能丢 | 每个区域对自己写入条目的副本 | 写按归属区域天然分开或接受 last writer wins 的全球产品 | 两地改同一条无声丢一个;别处读旧值;事务只在区域内 |
备份恢复和**引燃灯(pilot light)**是主备光谱上更便宜的两端;跨区域法定人数的数据库(多数副本跨区域确认后写才提交)是写本地复制的强一致替代;区域配对是云商用来排恢复顺序的分组,不改变你的设计。它们在这里解释,不单独画。单个数据库内部的复制机制见 数据库复制架构,灾难恢复策略的流程见 Disaster Recovery。
1. Single-region Multi-zone:一个区域两个可用区,主库在一个区,同步备库在另一个区
AWS 的文档把地基写清了:「每个 Region 都设计为与其他 Region 隔离」;「Region 之间相互隔离,我们不会自动跨 Region 复制资源」;「每个 Region 有多个隔离的位置,称为可用区」;「通过在多个可用区启动 EC2 实例,你可以保护应用免受 Region 内单个位置故障的影响」;「如果你把实例分布在多个可用区,一个实例故障时,你可以设计应用让另一个可用区的实例来处理请求」。灾备白皮书说清了它的边界:「一个 AWS Region 内的可用区已经被设计成彼此有足够距离,并经过位置的仔细规划,使得大多数常见灾难只影响一个可用区而不影响其他可用区。因此,一个 Region 内的多可用区架构可能已经满足你大部分的风险缓解需求」;「如果你对灾难的定义超出了一个物理数据中心的中断或丢失、达到了一个 Region 的程度,或者你受到要求如此的监管,那么你应该考虑 Pilot Light、Warm Standby 或 Multi-Site Active/Active」。
应用部署在两个可用区,前面是区域级负载均衡,只把请求分给活着的区;主库在 zone a,备库在 zone b,主库等备库确认写入之后才向应用报告提交。zone a 整个断电:负载均衡把流量转到 zone b,备库提升为主库,入口地址不变,已确认的写一条不丢,代价是每次写多一趟跨区往返。整个区域不可用:没有别处可去,产品停服到区域恢复;备份如果也只在这个区域,连恢复都做不到。
典型事故:zone a 断电,主库和一半应用同时消失——负载均衡按健康检查摘掉 zone a,同步备库提升,zone b 的实例扩容到能独自扛全部流量,所以两个区平时都要承载流量、都要能单独扛全量。区域级网络事件让所有可用区同时不可达——这不是故障处理能解决的,是这个拓扑的边界;把备份和基础设施定义放到另一个区域,至少能在别处重建。
2. Active-passive with Warm Standby:主区域全量服务,备区域缩容运行、异步复制,DNS 按健康检查切换
白皮书的定义:「warm standby 方法是确保在另一个 Region 有一个缩小规模但功能完整的生产环境副本」;「区别在于 pilot light 不先采取额外动作就无法处理请求,而 warm standby 可以立即处理流量(以降低的容量水平)」;「主动/被动策略用一个活动站点(例如一个 AWS Region)托管工作负载并服务流量。被动站点(例如另一个 AWS Region)用于恢复。被动站点在触发故障切换事件之前不主动服务流量」。两个目标的定义:「恢复时间目标(RTO)是服务中断与服务恢复之间可接受的最大延迟」;「恢复点目标(RPO)是距离上一次数据恢复点可接受的最长时间」;并且「如果恢复策略的成本高于故障或损失的成本,除非有监管要求这类次要驱动因素,否则不应该部署该恢复方案」。
切流量:主备 DNS 切换下「响应查询时,Route 53 只包含健康的主资源。如果所有主资源都不健康,Route 53 开始在 DNS 响应里只包含健康的备资源」;白皮书提醒「基于健康检查或告警自动触发的故障切换应谨慎使用……如果你在不需要的时候切换了(误报),你就白白承受了那些损失。因此常用人工触发的故障切换。这种情况下你仍应把切换步骤自动化,让人工触发像按一下按钮」;「为了最大限度的弹性,故障切换操作应只使用数据面的操作」。数据库这边,Aurora Global Database 有「一个写入数据的主 AWS Region,以及最多 10 个只读的从 Region」,复制「延迟通常低于一秒」;计划外故障用「Failover……这种方法的 RPO 通常是以秒计的非零值。数据丢失量取决于故障时刻 Aurora 全局数据库跨 AWS Region 的复制延迟」;计划内切换用「Switchover……因为该功能在做任何其他变更之前先把从集群与主集群同步,RPO 为 0(无数据丢失)」;「托管故障切换不会等待所选从 Region 与当前主 Region 之间的数据同步」;「因为写围栏是尽力而为,旧主 Region 可能短暂地仍接受写入,造成脑裂」;「如果你使用全局写入端点且应用或网络层缓存 DNS 值,把 DNS 缓存的 TTL 降到很低的值,例如 5 秒」;RPO 还能强制:Aurora「如果所有从集群的 RPO 延迟都大于 RPO,则阻塞该事务」。Azure 给云商侧的分组起了名:「每个区域对里有一个区域被优先恢复」;「Azure 力求把计划内的系统更新错开到区域对上」;但「把资源部署到配对区域并不会自动让它们更有弹性,也不提供自动的高可用、灾难恢复能力或故障切换。无论是否使用配对区域,都要制定你自己的高可用和灾难恢复计划」。
典型事故:主区域整体不可用,副本落后三秒——健康检查失败后 DNS 只答备区域,提升副本,备区域应用扩容;缓存了旧答案的客户端 TTL 过期后陆续过来;那三秒的写不在新主库上,旧区域恢复后先对存储做故障点快照从里面找回,再加回作为备区域,之后用零丢失的 switchover 切回去。一次网络抖动让健康检查连续失败,自动切换提升了副本,主区域其实还健康、缓存旧答案的客户端继续往它写——两边同时接受写入,各有对方没有的订单;提升前先围栏旧主库,TTL 设短,切换由人触发、步骤自动化,分叉的写按业务规则对账合并。
3. Active-active · Read Local, Write Global:每个区域就近读自己的副本,所有写转发到唯一主区域
白皮书的命名:「常见做法是把用户的读设计为由离他们最近的 Region 服务,称为 read local」;「write global 策略把所有写路由到单个 Region。该 Region 故障时,另一个 Region 会被提升以接受写入」;「Aurora 还支持写转发,让 Aurora 全局数据库里的从集群把执行写操作的 SQL 语句转发到主集群」;「你可以在不到一分钟内把某个从 Region 提升为承担读写职责」。切流量:「在主动-主动故障切换里,所有名称相同、类型相同(如 A 或 AAAA)、路由策略相同(如加权或延迟)的记录都是活动的,除非 Route 53 认为它们不健康。Route 53 可以用任何健康的记录回答 DNS 查询」;路由策略包括「基于地理邻近和延迟的策略」。
用户按延迟路由到最近区域,读走该区域的副本,几毫秒返回;写被转发到主区域的应用、写进全局主库并在那里提交,再异步复制回各区域。写永远不冲突,因为只有一个地方在写;代价是每次写跨越世界一个来回,主区域是写可用性的单点。主区域没了:各地的读照常,各地的写全部失败,直到 lag 最小的副本被提升、转发和全局写入口改指过去。
典型事故:主区域 A 挂了——B 区用户照常打开页面,一点保存就报错;应用在这段时间明确返回「暂时只读」而不是超时,提升后旧区域作为副本加回并从故障点快照找回最后一个 lag 的写。用户刚改完设置一刷新,B 区副本还没收到,看到的是旧值,以为没保存又改一次——写后一小段时间把该用户的读固定到主区域,或直接用写的返回结果渲染,把复制延迟做成指标告警。
4. Active-active · Write Local:每个区域写自己的副本、双向复制,冲突靠 last writer wins 或按归属区域分写
白皮书:「write local 策略把写路由到最近的 Region(和读一样)。Amazon DynamoDB 全局表支持这种策略,允许从全局表部署的每个 Region 读写。Amazon DynamoDB 全局表在并发更新之间使用 last writer wins 调和」;「write partitioned 策略按分区键(如用户 ID)把写分配给特定 Region,以避免写冲突」。全局表的文档:「应用向某个 Region 的副本写入数据时,DynamoDB 自动把该写入复制到全局表里的所有其他副本」;最终一致模式下「MREC 全局表副本里的条目变更被异步复制到所有其他副本,通常在一秒或更短时间内」;「如果同一条目在多个 Region 同时被修改,DynamoDB 会按条目使用内部时间戳最新的那次修改来解决冲突,称为 last writer wins 冲突解决方法」;「强一致读操作在条目最后一次更新发生在读取所在 Region 时返回最新版本,但如果条目最后一次更新在另一个 Region,可能返回过期数据」;「MREC 全局表的恢复点目标(RPO)等于副本之间的复制延迟」;事务「只在调用该操作的 Region 内是原子的」。强一致替代:「MRSC 全局表副本里的条目变更会在写操作返回成功响应之前同步复制到至少一个其他 Region」;这种表「必须恰好部署在三个 Region」,「写和强一致读的延迟会更高」,并支持「RPO 为零」。法定人数数据库描述的是同一笔交易:Spanner 的多区域配置有「一个默认 leader region,意味着它包含你数据库的 leader 副本」,「每次客户端向数据库发出变更,就会形成一个写法定人数,由默认 leader region 的一个副本和另外四个投票副本中的任意两个组成」,提供「99.999% 的可用性,高于 Spanner 区域配置提供的 99.99%」,「你的应用在更多地方获得更快的读,代价是写延迟略有增加」。CockroachDB:「region 级生存目标的性质是,即使整个 region 不可用,数据库仍能完全可读可写」;「要在 region 故障中生存,你必须添加至少 3 个数据库 region」;「写延迟至少会增加到最近 region 的往返时间那么多」;「对于写,CockroachDB 需要在 3 个 region 中的 2 个之间协调」。
没有主区域。每个区域的应用把写落在自己的副本上立刻返回,双向复制把变更送去对方;读写都本地。同一条记录在复制延迟之内被两个区域各改一次,两个副本收到对方的版本时只能留一个,按时间戳晚的赢,另一个更新无声消失。要么接受,要么从设计上让冲突不可能:给每条记录一个归属区域,只有归属区域写它,其他区域的写转到归属区域;不能分归属的记录用条件写,或改用跨区域法定人数的强一致模式。
典型事故:用户在 A 区改地址,同一秒客服在 B 区给同一条记录加备注,两边就地写——时间戳晚的赢,用户的新地址无声丢了;归属规则本可以让 B 区把写转到 A。区域间网络断了十分钟,B 区照常写,恢复后十分钟的积压一次性回放到 A,一批更新在两边悄悄消失——监控每对区域的复制延迟,超阈值时把流量从隔离的区域引开只服务读,恢复后把回放窗口内的冲突导出复核。
四种拓扑共同的底线
- 说清不用人干预能扛住什么。 一个可用区、一个区域但要切换、读扛区域而写要提升,还是读写都扛。
- 说清写落到哪才算数。 同步备库确认后、主区域本地提交、全局主库提交,还是本地副本立刻;这决定了第二个区域会不会落后、落后多少。
- 说清谁切流量、客户端多久才过来。 负载均衡的健康检查、DNS 的健康检查加 TTL、延迟路由加提升,还是根本没有切换。
- 把 RPO 和 RTO 写成数字并持续测量。 复制延迟对 RPO、健康检查和 TTL 对 RTO、备区域容量和配额对峰值,都要有指标和告警。
- 定期真的演练一次。 用数据面的开关切换,切换前围栏旧主库,切回用零丢失的 switchover;秘钥、配额和权限在每个区域提前备好。
故障与恢复
| 架构 | 故障 | 用户看到什么 | 恢复 |
|---|---|---|---|
| Multi-zone | zone a 断电 | 几秒抖动 | 负载均衡转到 zone b;同步备库提升;零丢失 |
| Multi-zone | 整个区域不可用 | 超时到区域恢复 | 没有切换目标;从异地备份重建;这是拓扑的边界 |
| Warm Standby | 主区域挂,副本落后 | 最后几秒的写不见了 | 提升 lag 最小的副本;RPO 等于 lag;从故障点快照找回 |
| Warm Standby | 客户端缓存旧 DNS 答案 | 部分用户仍超时 | 切换记录用短 TTL;数据面开关 |
| Warm Standby | 误报切换、旧主库没围栏 | 两边各有对方没有的订单 | 提升前围栏;人触发步骤自动化;按业务规则对账合并 |
| Read Local, Write Global | 主区域挂 | 能看不能存 | 读照常;提升副本;改指转发和写入口;期间明确只读 |
| Read Local, Write Global | 读己之写读到旧值 | 以为没保存 | 写后短时间固定读到主区域,或用写的返回值渲染 |
| Write Local | 两区同时改同一条 | 一个更新无声丢失 | 归属区域,只有归属区域写;条件写;或跨区域法定人数强一致 |
| Write Local | 区域被隔离后回放 | 一批更新悄悄消失 | 监控每对区域的延迟;隔离期只服务读;回放窗口冲突复核 |
怎么选
- 默认单区域多可用区:应用跨区、主库配另一个区的同步备库、备份放别的区域,并写下「区域没了就停服」。
- 区域没了不能停、能接受有界损失:Active-passive Warm Standby,异步复制到一个已经在跑的第二区域,RTO 和 RPO 写成签过字的数字,用数据面开关切换,TTL 设短,围栏旧主库,定期演练。
- 全球用户、读多写少、写不能冲突:Read Local, Write Global,读按延迟路由到区域副本,写转发到唯一主区域,接受跨区写延迟,提升流程提前演练。
- 记录有归属区域、或业务接受 last writer wins:Write Local,每条记录只在归属区域写,冲突靠设计杜绝而不是靠时间戳裁决,监控每对区域的复制延迟,写必须全局强一致时换跨区域法定人数的存储。
- 无论哪种:写下不用人能扛住什么、写落在哪才算数、谁切流量客户端多久过来、远方用户体验什么。
回到开头的四个产品:地区 SaaS 走 Single-region Multi-zone;受监管核心业务走 Warm Standby;全球内容产品走 Read Local, Write Global;全球各写各的走 Write Local。
面试时这样回答
- 先复述约束:要扛住一个可用区还是一个区域、能丢多少秒、停多少分钟、用户在哪、写会不会冲突。
- 说请求路径:点名图上的边,例如「负载均衡分到 zone a,主库等 zone b 的同步备库确认再提交」「DNS 按健康检查答主区域,主库本地提交后异步复制到备区域副本」「读本地副本,写转发到主区域提交再复制回来」「写本地副本立刻返回,双向复制相遇时谁晚谁赢」。
- 说状态归谁:zone a 的主库、主区域的主库、全局主库,还是各区域对自己归属条目的副本。
- 说代价与一个故障:例如主区域挂了副本落后三秒,修法是提升 lag 最小的副本、短 TTL、数据面开关、旧区域恢复后从故障点快照找回那三秒、再用 switchover 零丢失切回。
一手证据
- Amazon EC2:Regions and Zones
- AWS Whitepaper:Disaster recovery options in the cloud
- AWS Whitepaper:Business Continuity Plan (BCP)
- Amazon Route 53:Active-active and active-passive failover
- Amazon Route 53:Configuring DNS failover
- Amazon DynamoDB:How DynamoDB global tables work
- Amazon Aurora:Using Amazon Aurora Global Database
- Amazon Aurora:Using switchover or failover in Amazon Aurora Global Database
- Google Cloud Spanner:Regional and multi-region configurations
- CockroachDB:Multi-Region Survival Goals
- Microsoft Learn:Azure region pairs and nonpaired regions