编辑
2026-05-09
记录知识
0

目录

架构的高可用
复杂度的真正来源
冗余的两种形态
计算冗余
存储冗余
计算高可用:从单机到多机
双机架构的直接成本
主备 vs 主主
存储高可用:被忽视的代价
传输延迟是本质障碍
传输线路故障
CAP 定理的现实意义
状态决策:被低估的难题
三种决策模式
过半数规则的代价
状态决策的工程实践
心跳检测的困境
应对策略
实用的高可用设计原则
1. 搞清楚 SLO 再动手
2. 消除单点,但别过度
3. 优雅降级优先于完美
4. 设计时考虑"故障域"
总结
参考

架构的高可用

高可用(High Availability)在维基百科中的定义是:

系统无中断地执行其功能的能力

但这个定义容易让人产生误解——以为高可用是要让系统"永远不宕机"。实际上,任何硬件都会故障,任何软件都会有 bug,物理定律决定了故障不可避免。

真正的高可用不是"不故障",而是"快速恢复"。99.99% 的可用性意味着每年约 52 分钟的停机时间,这个目标才现实可及。

复杂度的真正来源

高可用之所以成为架构复杂度的来源,是因为它的本质手段——冗余——会引发连锁反应:

故障不可避免 ↓ 冗余(增加副本) ↓ 副本间的状态同步 ↓ 状态一致性挑战 ↓ 决策机制的复杂性 ↓ 引入更多边界情况

每一步都可能出错,每一次防护都可能引入新的问题。


冗余的两种形态

计算冗余

计算的本质是纯函数——相同的输入在哪里执行,输出都一样。这使得计算可以自由迁移:

请求 A → 任意副本 → 结果相同

计算冗余的实现相对简单,因为副本之间不需要共享状态。

存储冗余

存储的本质是有状态——数据必须保持一致。存储副本之间必须同步,而同步需要时间:

写入节点 A(耗时 X ms) ↓ 同步到节点 B(耗时 Y ms) ↓ 节点 B 的数据才可用

毫秒级的延迟对人类来说无感,但对系统状态一致性来说是本质性的挑战。


计算高可用:从单机到多机

双机架构的直接成本

单机升级为双机,首要增加的是任务分配器(Load Balancer)。

但分配器本身也需要高可用,于是引入一个问题:分配器的高可用又如何保证?

几种常见的分配策略:

策略描述适用场景
DNS 轮询简单,但生效慢勉强可用
L4 负载均衡性能好,但贵高并发
L7 负载均衡功能强,灵活复杂路由

主备 vs 主主

双机部署有两种基本模式:

主备(Active-Standby)

  • 一台主处理业务,一台备用
  • 备机只有在故障时才接管
  • 资源利用率 50%

主主(Active-Active)

  • 两台都处理业务
  • 需要会话同步
  • 资源利用率高,但复杂度也高

备机的备份程度又分三种:

类型备机状态切换速度资源消耗
冷备完全关闭慢(需要启动)最低
温备接收请求但不处理
热备同步接收所有请求最高

存储高可用:被忽视的代价

传输延迟是本质障碍

数据同步需要经过网络,而网络的延迟是物理限制:

场景延迟量级
同机房1-5 ms
同城多机房10-30 ms
跨区域(北上广)30-50 ms
跨国100-500 ms

这意味着在某个时间窗口内,系统数据必然不一致。这个时间窗口无法消除,只能缩小。

传输线路故障

物理线路会中断,而且恢复时间长:

事件影响时间
挖断光缆几小时
海底光缆故障几小时
BGP 故障几十分钟

2015 年支付宝某次故障就是因为光缆被挖断,业务中断超过 4 小时。

CAP 定理的现实意义

CAP 定理说:分布式系统无法同时满足一致性(Consistency)、可用性(Availability)、分区容错(Partition tolerance)。

对存储高可用的实际指导意义:

业务场景优先级说明
金融、账务C > A宁可不可用,也要数据一致
社交FeedA > C用户体验优先,允许短暂不一致
日志、监控P 优先允许丢失,但不能不可用

没有"最佳"方案,只有"适合业务"的方案。


状态决策:被低估的难题

高可用的核心之一是判断当前状态——谁活着?谁该接管?

但讽刺的是:状态决策本身无法自举

如果决策者也需要状态决策,那就陷入无限递归。

三种决策模式

独裁式

上报者 A ──┐ 上报者 B ──┼──▶ 决策者 ──▶ 决策 上报者 C ──┘

单一决策者,决策高效。但决策者本身是单点故障。

协商式

Server A ◀───连接────▶ Server B │ │ └──────协商决策─────────┘

通过连接交换信息,按规则决策。两台机器协商主备关系。

致命问题:连接断开时怎么办?

  • 备机认为主机挂了,自己升为主机 → 双主
  • 备机保守不升为主机 → 主机真挂了就没有主了

民主式

投票轮询 → 多数者胜出 A ◀──▶ B ◀──▶ C

每个节点互通信息,通过投票选出主节点。

致命问题:脑裂——网络分区后,两个子集群各自选举,产生两个主节点。

过半数规则的代价

民主式决策通过"获得过半票数才能当选"来防止脑裂:

5节点集群:需要 3 票才能当选 分区后: - 分区1(3节点):3 >= 3 ✓ 选出主 - 分区2(2节点):2 < 3 ✗ 无法选举

这个规则解决了脑裂,但代价是降低了可用性——当节点故障导致可用节点不足时,系统彻底不可用,而不是降级。


状态决策的工程实践

心跳检测的困境

协商式决策依赖心跳来检测对方存活,但心跳本身不可靠:

心跳中断原因实际状态
对方真的挂了需要切换
网络抖动假故障
心跳本身丢包假故障
自己的网络卡误判

无法区分"对方挂了"和"网络断了"——这两种情况的正确应对完全不同。

应对策略

策略1:保守降级

  • 连接中断时保持备机状态
  • 宁可少一个主,也不冒险双主
  • 代价:真正故障时切换慢

策略2:过半数裁决

  • 不信任的节点不参与投票
  • 只有获得过半承认才能当选
  • 代价:小分区永远选不出主

策略3:红黄蓝灯机制

红线(立即切换):对方明确声明故障 黄灯(延迟观察):心跳超时,等待确认 蓝灯(保持现状):网络抖动,不做动作

复杂但更健壮。


实用的高可用设计原则

1. 搞清楚 SLO 再动手

SLO年停机时间实现难度
99%3.65 天简单
99.9%8.76 小时中等
99.99%52 分钟困难
99.999%5 分钟极高

每提高一个 9,成本可能翻倍。先问自己:业务真的需要这么高可用吗?

2. 消除单点,但别过度

画出现有架构图,标记每个组件的"唯一性":

  • 只有一个数据库?→ 需要主从
  • 只有一个缓存?→ 需要集群
  • 只有一个负载均衡器?→ 需要 VRRP/Keepalived

但不要为不存在的流量担忧。过度设计比欠设计的代价更高。

3. 优雅降级优先于完美

当故障发生时:

❌ 争取完美:系统全部功能必须正常 ✅ 接受降级:核心功能正常,非核心功能暂时不可用

微博热搜挂了不影响发微博。支付失败了可以提示重试。

4. 设计时考虑"故障域"

把故障限制在局部,不要扩散:

  • 机房级别故障 → 多机房部署
  • 服务器级别故障 → 多副本 + 自动摘除
  • 进程级别故障 → Supervisor 重启
  • 请求级别故障 → 超时 + 重试 + 熔断

总结

高可用的核心矛盾在于:故障不可避免,但我们需要系统"看起来"永远可用。这个矛盾的解法——冗余——本身引入了状态同步、状态决策、一致性保障等复杂性。

关键认识:

  1. 高可用不是"不故障",而是"快速恢复"
  2. 冗余是手段,状态同步是代价
  3. 计算冗余简单,存储冗余复杂(受延迟和一致性约束)
  4. 状态决策无法完美,只能权衡
  5. CAP 定理是起点,不是终点
  6. 脑裂是分布式系统的固有难题,没有完美解法
  7. 高可用设计是成本收益分析,不是追求极致

没有银弹。只有理解了这些复杂性的来源,才能做出明智的架构决策。


参考

  • 软件架构基础