06 高可用架构

0.1. CAP

对于设计分布式系统的架构师来说,CAP 是必须掌握的理论

在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。

分布式并不一定会互联和共享数据

  • CAP探讨的对象是interconnectedshare data的分布式系统。
  • CAP关注的是对数据的读写操作,而不是分布式系统所有功能。

0.1.1. 定义

特性原始解释升级版解释
一致性(Consisitency)所有节点在同一时刻都能看到相同的数据对某个指定的客户端来说,读操作保证能够返回最新的写操作结果
可用性(Availability)每个请求都能得到成功或者失败的响应非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)
分区容错性(Partition Tolerance)出现消息丢失或者分区错误时系统能够继续运行当出现网络分区后,系统能够继续“履行职责”

一致性:,对于系统执行事务来说,在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致,也就是出现same time + different data的情况,而不是每时每刻都是same time + same data

网络分区:在分布式集群中,节点之间由于网络不通,导致集群中节点形成不同的子集,子集中节点间的网络相通,而子集和子集间网络不通。

0.1.2. 应用

虽然 CAP 理论定义是三个要素中只能取两个,但放到分布式环境下必须选择 P(分区容忍)要素,因为网络本身无法做到 100% 可靠,有可能出故障,所以分区是必然的现象

假设实现 CA 架构,当发生分区现象时,为了保证 C,系统需要禁止写入,当有写入请求时,系统返回 error(例如,当前系统不允许写入),这又和 A 冲突了,因为 A 要求返回 no errorno timeout。因此,分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。

0.1.2.1. CP-Consistency/Partition Tolerance

如下图所示,为了保证一致性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x

这时客户端访问 N2 时,N2 需要返回 Error,提示客户端“系统现在发生了错误”,这种处理方式违背了可用性(Availability)的要求,因此 CAP 三者只能满足 CP。

image

0.1.2.2. AP-Availability/Partition Tolerance

如下图所示,为了保证可用性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x

这时客户端访问 N2 时,N2 将当前自己拥有的数据 x 返回给客户端,而实际上当前最新的数据已经是 y,这就不满足一致性(Consistency)的要求,因此 CAP 三者只能满足 AP。

注意:这里 N2 节点返回 x,虽然不是一个“正确”的结果,但是一个“合理”的结果,因为 x 是旧的数据,并不是一个错乱的值,只是不是最新的数据而已。

image

0.1.3. CAP关键细节点

0.1.3.1. CAP关注的粒度是数据,而不是整个系统

C 与 A 之间的取舍可以在同一系统内以非常细小的粒度反复发生,而每一次的决策可能因为具体的操作,乃至因为牵涉到特定的数据或用户而有所不同。

CAP 理论的定义和解释中,都是 systemnode 这类系统级的概念,这造成了误导,认为在进行架构设计时,整个系统要么选择 CP,要么选择 AP。

在实际设计过程中,每个系统不可能只处理一种数据,而是包含多种类型的数据:

  • 有的数据必须选择 CP
  • 有的数据必须选择 AP

架构设计时,从整个系统的角度去选择 CP 还是 AP,就会发现顾此失彼。所以在 CAP 理论落地实践时,需要将系统内的数据按照不同的应用场景和要求进行分类,每类数据选择不同的策略(CP 还是 AP),而不是直接限定整个系统所有数据都是同一策略。

0.1.3.2. CAP是忽略网络延迟的

CAP 理论中的 C 在实践中是不可能完美实现的,在数据复制的过程中,总是需要花费一定的时间(几毫秒到几十毫秒不等),节点 A 和节点 B 的数据并不一致。

技术上是无法做到在分布式场景下完美的一致性的,但是在条件严格的业务场景下,必须要求一致性(如银行账户存取钱、商品抢购库存更新等),因此在实际中会选择CA,也就是单点写入,其他节点做备份,无法做到分布式情况下的多点写入。

例如,根据用户ID将用户的读写操作限定在某一个节点,这样单点故障只会影响部分用户,从整体上看依然是分布式架构。

0.1.3.3. 正常运行情况下,不存在 CP 和 AP 的选择,可以同时满足 CA

CAP 理论中分布式系统只能选择 CP 或者 AP的前提是系统发生了“分区”现象。

正常运行情况下,系统中不存在网络分区,节点间的网络连接一起正常,此时应该同时保证CA。因此,在架构设计的时候既要考虑分区发生时选择 CP 还是 AP,也要考虑分区没有发生时如何保证 CA

0.1.3.4. 放弃并不等于什么都不做,需要为分区恢复后做准备

CAP 理论中三者只能取两个,需要“牺牲”(sacrificed)另外一个,这里的“牺牲”是有一定误导作用的,因为“牺牲”让很多人理解成什么都不做。

实际上,CAP 理论的“牺牲”只是说在网络分区过程中无法保证 C 或者 A,但并不意味着什么都不做。因为在系统整个运行周期中,大部分时间都是正常的,发生分区现象的时间并不长。分区期间放弃 C 或者 A,并不意味着永远放弃 CA,可以在分区期间进行一些操作,从而让分区故障解决后,系统能够重新达到 CA 的状态。

最典型的做法就是在分区期间记录日志,当分区故障解决后,系统根据日志进行数据恢复,使得重新达到 CA 状态。

0.1.4. 与ACID和BASE的对比

0.1.4.1. ACID

ACID 是数据库管理系统为了保证事务的正确性而提出来的一个理论,ACID 包含四个约束。

  • Atomicity(原子性):一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
  • Isolation(隔离性):数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括:
    • 读未提交(Read uncommitted)
    • 读提交(read committed)
    • 可重复读(repeatable read)
    • 串行化(Serializable)
  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

  • ACID 中的 A(Atomicity)和 CAP 中的 A(Availability)意义完全不同

  • ACID 中的 C 和 CAP 中的 C 名称虽然都是一致性,但含义也完全不一样

    • ACID 中的 C 是指数据库的数据完整性
    • CAP 中的 C 是指分布式节点中的数据一致性
  • ACID 的应用场景是数据库事务

  • CAP 关注的是分布式系统数据读写

0.1.4.2. BASE

BASE :

  • 基本可用(Basically Available)
  • 软状态(Soft State)
  • 最终一致性(Eventual Consistency)

核心思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。

  1. 基本可用(Basically Available):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。具体选择哪些作为可以损失的业务,哪些是必须保证的业务,是一项有挑战的工作。
  2. 软状态(Soft State):允许系统存在中间状态(CAP 理论中的数据不一致),而该中间状态不会影响系统整体可用性。
  3. 最终一致性(Eventual Consistency):系统中的所有数据副本经过一定时间(这里的“一定时间”和数据的特性是强关联的,不同的数据能够容忍的不一致时间是不同的)后,最终(不管多长时间)能够达到一致的状态。

BASE 理论本质上是对 CAP 中 AP 方案的一个补充:

  • CAP 理论是忽略延时的,而实际应用中延时是无法避免的。这意味着完美的 CP 场景是不存在的,即使是几毫秒的数据复制延迟,在这几毫秒时间间隔内,系统是不符合 CP 要求的。因此 CAP 中的 CP 方案,实际上也是实现了最终一致性,只是“一定时间”是指几毫秒。
  • AP 方案中牺牲一致性只是指分区期间,而不是永远放弃一致性。这其实就是 BASE 理论延伸的地方,分区期间牺牲一致性,但分区故障恢复后,系统应该达到最终一致性。

综上:

  • ACID 是数据库事务完整性的理论
  • CAP 是分布式系统设计理论
  • BASE 是 CAP 理论中 AP 方案的延伸

0.2. 排除架构可用性隐患

FMEA(Failure mode and effects analysis,故障模式与影响分析)又称为失效模式与后果分析、失效模式与效应分析、故障模式与后果分析等,FMEA 是一种在各行各业都有广泛应用的可用性分析方法,通过对系统范围内潜在的故障模式加以分析,并按照严重程度进行分类,以确定失效对于系统的最终影响。

在架构设计领域,FMEA 的具体分析方法是:

  1. 给出初始的架构设计图。
  2. 假设架构中某个部件发生故障。
  3. 分析此故障对系统功能造成的影响。
  4. 根据分析结果,判断架构是否需要进行优化。

FMEA 分析的方法是分析表,常见的 FMEA 分析表格包含下面部分。

0.2.1. 功能点

注意这里的“功能点”指的是从用户角度来看的,而不是从系统各个模块功能点划分来看的。

0.2.2. 故障模式

系统会出现什么样的故障,包括故障点故障形式

注意,这里的故障模式并不需要给出真正的故障原因,只需要假设出现某种故障现象即可,例如 MySQL 响应时间达到 3 秒。造成这个现象可能的原因很多:磁盘坏道、慢查询、服务器到 MySQL 的连接网络故障、MySQL bug 等,不需要在故障模式中一一列出来,而是在“故障原因”一节中列出来。

因为在实际应用过程中,不管哪种原因,只要现象是一样的,对业务的影响就是一样的

此外,故障模式的描述要尽量精确,多使用量化描述,避免使用泛化的描述。例如,推荐使用“MySQL 响应时间达到 3 秒”,而不是“MySQL 响应慢”。

0.2.3. 故障影响

当发生故障模式中描述的故障时,功能点具体会受到什么影响。常见的影响有:

  • 功能点偶尔不可用、
  • 功能点完全不可用、
  • 部分用户功能点不可用、
  • 功能点响应缓慢、
  • 功能点出错等。

故障影响需要准确描述。例如,推荐使用“20% 的用户无法登录”,而不是“大部分用户无法登录”。要注意这里的数字不需要完全精确,比如 21.25% 这样的数据其实是没有必要的,只需要预估影响是 20% 还是 40%。

0.2.4. 严重程度

站在业务角度故障的影响程度,一般分为“致命 / 高 / 中 / 低 / 无”五个档次。

严重程度按照这个公式进行评估:严重程度 = 功能点重要程度 × 故障影响范围 × 功能点受损程度

以用户管理系统为例:登录功能比修改用户资料要重要得多,80% 的用户比 20% 的用户范围更大,完全无法登录比登录缓慢要更严重,得出如下故障模式的严重程度。

  • 致命:超过 70% 用户无法登录。
  • 高:超过 30% 的用户无法登录。
  • 中:所有用户登录时间超过 5 秒。
  • 低:10% 的用户登录时间超过 5 秒。
  • 中:所有用户都无法修改资料。
  • 低:20% 的用户无法修改头像。

对于某个故障的影响到底属于哪个档次,有时会出现一些争议。

0.2.5. 故障原因

“故障模式”中只描述了故障的现象,不管什么故障原因,故障现象相同,对功能点的影响就相同。

单独将故障原因列出来是原因:

  1. 不同的故障原因发生概率不同,而不同的概率又会影响具体如何应对这个故障。
  2. 不同的故障原因检测手段不同
  3. 不同的故障原因处理措施不同

0.2.6. 故障概率

指某个具体故障原因发生的概率。例如,磁盘坏道的概率、MySQL bug 的概率、没有索引的概率。

一般分为“高 / 中 / 低”三档即可,具体评估时有以下几点要重点关注。

  1. 硬件:硬件随着使用时间推移,故障概率会越来越高。
  2. 开源系统:
    1. 成熟的开源系统 bug 率低,刚发布的开源系统 bug 率相比会高一些;
    2. 已经有使用经验的开源系统 bug 率会低,刚开始尝试使用的开源系统 bug 率会高。
  3. 自研系统:成熟的自研系统故障概率会低,而新开发的系统故障概率会高。

高中低是相对的,只是为了确定优先级以决定后续的资源投入,没有必要绝对量化,因为绝对量化是需要成本的,而且很多时候都没法量化。

0.2.7. 风险程度

综合严重程度和故障概率判断某个故障的最终等级,风险程度 = 严重程度 × 故障概率。可能出现某个故障影响非常严重,但其概率很低,最终来看风险程度就低。

“某个机房业务瘫痪”对业务影响是致命的:

  • 如果故障原因是“地震”,那概率就很低,例如广州5 级以上地震的 20 世纪才 1 次(1940 年);
  • 如果故障的原因是“机房空调烧坏”,则概率就比地震高很多了,可能是 2 年 1 次;
  • 如果故障的原因是“系统所在机架掉电”,这个概率比机房空调又要高了,可能是 1 年 1 次。

同样的故障影响,不同的故障原因有不同的概率,最终得到的风险级别就是不同的。

0.2.8. 已有措施

针对具体的故障原因,系统现在是否提供了某些措施来应对,包括:检测告警、容错、自恢复等。

  • 检测告警:最简单的措施就是检测故障,然后告警,系统自己不针对故障进行处理,需要人工干预。
  • 容错:检测到故障后,系统能够通过备份手段应对。例如,MySQL 主备机,当业务服务器检测到主机无法连接后,自动连接备机读取数据。
  • 自恢复:检测到故障后,系统能够自己恢复。主要指“业务”上的恢复,一般不太可能将真正的故障恢复。

0.2.9. 规避措施

为了降低故障发生概率而做的一些事情,可以是技术手段,也可以是管理手段

  • 技术手段:为了避免新引入的 MongoDB 丢失数据,在 MySQL 中冗余一份。
  • 管理手段:为了降低磁盘坏道的概率,强制统一更换服务时间超过 2 年的磁盘。

0.2.10. 解决措施

为了能够解决问题而做的一些事情,一般都是技术手段。例如:

  • 为了解决密码暴力破解,增加密码重试次数限制。
  • 为了解决拖库导致数据泄露,将数据库中的敏感数据加密保存。
  • 为了解决非法访问,增加白名单控制。

如果某个故障既可以采取规避措施,又可以采取解决措施,那么优先选择解决措施

如果问题是系统自己无法解决的,例如磁盘坏道、开源系统 bug,这类故障只能采取规避措施;系统能够自己解决的故障,大部分是和系统本身功能相关的。

0.2.11. 后续规划

综合前面的分析:

  1. 看出哪些故障目前还缺乏对应的措施,
  2. 哪些已有措施还不够,针对这些不足的地方,再结合风险程度进行排序,给出后续的改进规划。

这些规划既可以是技术手段,也可以是管理手段;可以是规避措施,也可以是解决措施。需要考虑资源的投入情况,优先将风险程度高的系统隐患解决。

例如:

  • 地震导致机房业务中断:这个故障模式就无法解决,只能通过备份中心规避,尽量减少影响;而机柜断电导致机房业务中断:可以通过将业务机器分散在不同机柜来规避。
  • 敏感数据泄露:这个故障模式可以通过数据库加密的技术手段来解决。
  • MongoDB 断电丢数据:这个故障模式可以通过将数据冗余一份在 MySQL 中,在故障情况下重建数据来规避影响。
上次修改: 4 June 2020