# 架构学习书摘总结（三）高可用架构模式

> 作者：zning（https://ning.ae） · 发布：2019/03/09 21:01 下午 (UTC+8) · 原文：https://zning.me/15521364926344.html

最近阅读了一本架构方面的入门图书叫《从零开始学架构：照着做，你也能成为架构师》，部分内容比较不错，先做书摘总结，以便加深印象与未来回顾学习。

本文是该书第三部分，是书中第六至十章，主要介绍CAP、FMEA、存储高可用、计算高可用、业务高可用，涉及到CAP理论、FMEA理论、主备复制倒换、异地多活等内容。

连续阅读，请点击如下链接：

* [架构学习书摘总结（一）概念和基础部分](./15511868502157.html)

* [架构学习书摘总结（二）高性能架构模式](./15517030839592.html)

* [架构学习书摘总结（三）高可用架构模式](./15521364926344.html)

* [架构学习书摘总结（四）可扩展架构模式](./15599222898499.html)

* [架构学习书摘总结（五）架构实战](./15600074562241.html)

## 第六章 CAP

1. CAP理论：**在一个分布式系统（指互相连接并共享数据的节点的集合）中，当涉及读写操作时，只能保证一致性（Consistence）、可用性（Availability）、分区容错性（Partition Tolerance）三者中的一个，另外一个必须被牺牲**。
2. 一致性（Consistence）：**对某个指定的客户端来说，读操作保证能够返回最新的写操作结果**。
3. 可用性（Availability）：**非故障的节点在合理的时间内返回合理的响应（不是错误和超时的响应）**。
4. 分区容错性（Partition Tolerance）：**当出现网络分区后，系统能够继续“履行职责”**。
5. CAP应用：分布式系统理论上不可能选择CA架构，**只能选择CP或者AP架构**。

    1. **CP：**一致性/分区容错性
    2. **AP：**可用性/分区容错性

6. CAP的细节

    1. CAP关注的粒度是数据，而不是整个系统；

        1. 实际设计过程中，每个系统不可能只处理一种数据，而是包含多种类型的数据，有的数据必须选择CP，有的数据必须选择AP。
        2. 在CAP理论落地实践时，我们需要将系统内的数据按照不同的应用场景和要求进行分类，**每类数据选择不同的策略（CP还是AP），而不是直接限定整个系统所有数据都是同一策略**。
    2. CAP是忽略网络延迟的；

        1. **布尔鲁在定义一致性时，并没有将延迟考虑进去。**也就是说，当事务提交时，数据能够瞬间复制到所有节点。但实际情况下，从节点A复制数据到节点B，总是需要花费一定时间的。
        2. 而业务上必须要求一致性的业务场景例如和金钱相关的，技术上是无法做到分布式场景下完美的一致性的，因此单个用户的余额、单个商品的库存，理论上要求选择CP而实际上CP都做不到，只能选择CA。也就是说，只能单点写入，其他节点做备份，无法做到分布式情况下多点写入。
    3. 正常运行情况下，不存在CP和AP的选择，可以同时满足CA；
        1. CAP理论告诉我们分布式系统只能选择CP或AP，但其实这里的前提是系统发生了“分区”现象。如果系统没有发生分区现象，也就是说P不存在的时候（节点间的网络连接一切正常），我们没有必要放弃C或A，应该C和A都可以保证，这就要求**架构设计的时候既要考虑分区发生时选择CP还是AP，也要考虑分区没有发生时如何保证CA**。
    4. 放弃并不等于什么都不做，需要为分区恢复后做准备。
        1. **CAP理论的“牺牲”只是说在分区过程中我们无法保证C或A，但并不意味着我们什么都不做。**因为在系统整个运行周期中，大部分时间都是正常的，发生分区现象的时间并不长。
        2. 分区期间放弃C或A，并不意味着永远放弃C和A，我们可以**在分区期间进行一些操作，从而让分区故障解决后，系统能够重新达到CA的状态**。

7. ACID

    1. Atomicity（原子性）
    2. Consistency（一致性）
    3. Isolation（隔离性）
    4. Durability（持久性）
    5. 区别：

        1. ACID的A和CAP的A意义完全不同。
        2. ACID的C和CAP的C，前者指数据库的数据完整性，后者指分布式节点中的数据一致性。
        3. ACID的应用场景是数据库事务，CAP关注的是分布式系统数据读写。
8. BASE：其是Basically Available（基本可用）、Soft State（软状态）和Eventually Consistency（最终一致性）三个短语的缩写，其核心思想是即使无法做到强一致性（CAP的一致性就是强一致性），但应用可以采用适合的方式达到最终一致性（Eventually Consistency）。

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

        1. 这里的关键词是“一定时间”和“最终”，“一定时间”和数据的特性是强关联的，不同的数据能够容忍的不一致时间是不同的。
        2. “最终”的含义就是不管多长时间，最终还是要达到一致性的状态。

    4. BASE理论本质上是对CAP的延伸和补充，更具体地说，是**对CAP中AP方案的一个补充**。

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

## 第七章 FMEA

1. FMEA (Failure mode and effects analysis, 故障模式与影响分析)， 是一种在各行各业广泛应用的可用性分析方法，通过对系统范围内潜在的故障模式加以分析，并按照严重程度进行分类，以确定失效对于系统的最终影响。
2. FMEA是一套分析和思考的方法，而不是某个领域的技能或工具。FMEA并不是指导我们如何做架构设计，而是当我们设计出一个架构后，再使用FMEA对这个架构进行分析，看看架构是否还存在某些可用性的隐患。
3. FMEA的具体分析方法如下：
    1. 给出初始的架构设计图。
    2. 假设架构中某个部件发生故障。
    3. 分析此故障对系统功能造成的影响。
    4. 根据分析结果，判断架构是否需要进行优化。

    FMEA的分析方法就是一个FMEA分析表，常见分析表格包含如下部分：
    5. **功能点：**当前的FMEA分析设计哪个功能点，注意这里的“功能点”指的是从用户的角度来看的，而不是从系统各个模块功能点划分来看的。
    6. **故障模式：**故障模式指的是系统会出现什么样的故障，包括故障点和故障模式。这里的故障模式并不需要给出真正的故障原因，我们只需要假设出现某种故障现象即可。此外，故障模式的描述要尽量准确，多使用量化描述，避免使用泛化的描述。
    7. **故障影响：**当发生故障模式中描述的故障时，功能点具体会受到什么影响。常见的影响有：功能点偶尔不可用、功能点完全不可用、部分用户功能点不可用、功能点响应缓慢，功能点出错等。故障影响也需要尽量准确描述。要注意这里的数字不需要完全精准，比如21.25%这样的数据其实是没有必要的，我们只需要预估影响是20%还是40%。
    8. **严重程度：**严重程度指站在业务的角度，故障的影响程度，一般分为“致命/高/中/低/无”五个档次。对于某个故障的影响到底属于哪个档次，有时会出现一些争议。这个没有绝对标准，一般建议相关人员讨论确定即可。也不建议花费太多时间争论，争执不下时架构师裁定即可。
    9. **故障原因：**“故障模式”中只描述了故障的现象，并没有单独列出故障原因。主要原因在于不管什么故障原因，故障现象相同，对功能点的影响就相同。而单独将故障原因列出主要因为：不同的故障原因发生概率不同、不同的故障原因检测手段不一样、不同的故障原因的处理措施不一样。
    10. **故障概率：**这里的概率就是指某个具体故障原因发生的概率。具体评估的时候需要有以下几点需要重点关注：
        1. 硬件：硬件会随着使用时间推移，故障概率越来越高。
        2. 开源系统：成熟的开源系统bug率低，刚发布的开源系统bug率相比会高一些；自己已经有使用经验的开源系统bug率会低，刚开始尝试使用的开源系统bug率会高。
        3. 自研系统：和开源系统类似，成熟的自研系统故障概率会低，而新开发的系统故障率会高。
        而高中低是相对的，只是为了确定优先级以决定后续的资源投入，没有必要绝对量化。
    11. **风险程度：**是综合严重程度和故障概率来一起判断某个故障的最终等级，风险程度=严重程度×故障概率。
    12. **已有措施：**针对具体故障原因系统现是否提供某些措施应对，包括检测告警、容错、自恢复等。
    13. **规避措施：**指为了降低故障发生概率而做的一些事情，可以是技术手段，也可以是管理手段。
    14. **解决措施：**指为了能够解决问题而做的一些事，一般都是技术手段。一般来说，如果某个故障既可以采取规避措施，又可以采取解决措施，那么我们会优先选择解决措施，毕竟能解决问题当然是好的。系统能够自己解决的故障，大部分是和系统本身功能相关的。
    15. **后续规划：**综合前面的分析，就可以看出哪些故障我们目前还缺乏对应的措施，哪些已有措施还不够，针对这些不足的地方，再结合风险程度进行排序，给出后续的改进规划。

## 第八章 存储高可用

1. 存储高可用方案的本质都是通过将数据复制到多个存储设备，通过数据冗余的方式来实现高可用，其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题。

2. **常见的存储高可用架构有：主备、主从、主主、集群、分区**，每一种又可以根据业务的需求进行一些特殊的定制化功能，由此衍生出更多的变种。一般的，不同业务的定制功能难以通用化。

3. **主备复制：**最常见也是最简单的一种存储高可用方案。主备架构中“备机”主要还是祈祷一个备份作用，并不承担合集的业务读写操作。

    综合主备复制架构的优缺点，内部的后台管理系统使用主备复制架构的情况会比较多。因为这类系统的数据变更频率很低，即使在某些场景下丢失，也可以通过人工的方式补全。
4. **主从复制：**主机负责读写操作，从机只负责读操作，不负责写操作。

    综合主从复制架构的优缺点，一般情况下，写少读多的业务使用主从复制的存储架构比较多。例如：BBS、新闻网站这类业务，此类业务的读操作数量是写操作数量的10倍甚至100倍以上。

    1. 如果主从间延迟较多，恰好此时主机又宕机了，则可能丢失较多数据，因此对于复制延迟也不能掉以轻心。一般的做法是做复制延迟的监控措施，当延迟的数据量较大时及时报警，由人工干预处理。
    2. 主从复制同样有故障时需要人工干预的缺点，人工在执行回复操作的过程中也容易出错，因为这类操作并不常见，可能一年也就一两次。

1. **主备倒换与主从倒换：**此为了解决“主机故障后无法进行写操作”、“主机无法恢复时需要人工指定新主机角色”两个问题而产生。简单来说，就是基于主备复制和主从复制方案基础上增加“倒换”功能，即系统自动决定主机角色，并完成角色切换。常见主备倒换架构有：**互联式、中介式、模拟式**。
    1. 关键设计点：主备间状态判断（状态传递的渠道、状态检测的内容）、倒换决策（倒换时机、倒换策略、自动程度）、数据冲突解决。
    2. 倒换方案比主备、主从的复制方案不止是多了一个倒换功能那么简单，而是复杂度上升一个数量级。
    3. 互联式：主备机直接建立状态传递的渠道。其如果状态传递的通道本身有故障（例如网线被踢掉），那么备机也会认为主机故障了从而将自己升级为主机，而此时主机并没有故障，最终可能就出现两个主机。
    4. 中介式：在主备两者之外引入第三方中介，但其比互联式连接管理与状态决策上更简单。但其关键代价就在于如何实现中介本身的高可用。这就陷入了一个设计递归陷阱。

        当下MongoDB的Replica Set采取的就是这种方式。其有个仲裁节点（MongoDB(A)），仲裁节点是一种特殊的节点，它本身并不存储数据，主要的作用是决定哪一个备节点在主节点挂掉之后提升为主节点，所以客户端不需要连接此节点。即使只有一个备节点，但是仍然需要一个仲裁节点来提升备节点级别。

        另外，幸运的是，开源的ZooKeeper是一个很成熟的解决方案。其本身就是一个高可用的系统，在高可用架构中有很多用途，主备倒换的中介就可以用ZooKeeper来做状态同步。
        
    1. 模拟式：指主备机之间并不传递任何状态数据，而是备机模拟成一个客户端向主机发起模拟的读写操作，根据读写操作的响应情况来判断主机的状态。

1. **主主复制：**其是两天及其都是主机，互相将数据复制给对方，客户端可以任意挑选其中一台机器进行读写操作。如果采用本架构，必须保证数据能够双向复制，而很多数据是不能双向复制的，比如用户注册后生成的用户ID以及库存。

    综上，本架构对数据的设计有严格的要求，一般适合于那些临时性、可丢失、可覆盖的数据场景。例如用户登录产生的session数据，用户行为的日志数据，论坛的草稿数据等。

1. **数据集群：**集群就是多台机器组合在一起形成一个统一的系统，这里的多肽数量上至少是3台，相比而言，主备、主从都是2台机器。根据集群中机器承担的不同角色来划分，集群可以分为两类：数据集中集群、数据分散机群。
    1. 数据集中集群：与主备、主从架构类似，可称为数据集中集群为一主多备、一主多从。其整体复杂度更高体现在主机如何将数据复制给备机、备机如何检测主机状态、主机故障后如何决定新主机。其例为开源的ZooKeeper，ZooKeeper通过ZAB协议来解决上面的几个问题，但ZAB协议比较复杂（类似Paxos算法），如果我们自己去实现ZAB协议的话那么复杂度同样非常高。
    2. 数据分散集群：多个服务器组成一个集群，每台服务器都会负责存储一部分数据。同时，未来提升硬件利用率，每台服务器又会备份一部分数据。例子如Hadoop的实现就是独立的服务器负责数据分区的分配，这台服务器叫做Namenode。而Elasticsearch集群通过选举一台服务器来做数据分区的分配，叫做master node。
    3. 上面两种集群的不同点在于，数据分散集群中的每台服务器都可以处理读写请求，因此不存在数据集中集群中负责写的主机那样的角色。但在数据分区集群中，必须有一个角色来负责执行数据分配算法，这个角色可以是独立的一台服务器，也可以是集群自己选举出的一台服务器。
    4. 数据集中集群架构中，客户端只能将数据写到主机；数据分散机群架构中，客户端可以向任意服务器中读写数据。正是因为这个关键的差异，决定了两种集群的应用场景不同。一般来说，数据集中式集群适合数据量不大，集群机器数量不多的场景。例如ZooKeeper集群一般推荐5台及其左右，数据量是单台服务器就能够支撑。而数据分散式集群，由于其良好的可伸缩性，适合业务数据量巨大，集群机器数量庞大的业务场景。例如Hadoop集群、HBase集群，大规模的集群可以达到上百台甚至上千台服务器。

2. **数据分区：**对于一些影响非常大的灾难或事故，例如天灾人祸，基于硬件故障而设计的高可用架构不再适用，我们需要基于地理级别的故障来设计高可用架构，这就是数据分区架构产生的背景。

    数据分区指将数据按照一定的规则进行分区，不同分区分布在不同的地理位置上，每个分区存储一部分数据，通过这种方式来规避地理级别的故障所造成的巨大影响。
    
    设计一个良好的数据分区架构，需要从数据量（数据量越大分区规则越复杂）、分区规则（洲际分区、国家分区、城市分区）、复制规则（集中式、互备式、独立式）、等方面进行考虑。

## 第九章 计算高可用

1. 计算高可用的主要涉及目标是当出现部分硬件损坏时，计算任务能够继续正常运行。因此，计算高可用的本质是通过冗余来规避部分故障的风险。
2. **常见的计算高可用架构有：主备、主从、对称集群、非对称集群**。
3. 计算高可用架构的设计复杂度主要体现在任务管理方面。关键点有：哪些服务器可以执行任务、任务如何重新执行。需要注意的是：任务管理中经常出现的“任务分配器”是一个逻辑的概念，并不一定要求系统存在一个独立的任务分配器模块。
4. **主备：**主备架构是计算高可用最简单的架构，和存储高可用的主备复制架构类似，但要更简单，因为不需要数据复制。

    根据备机状态的不同，主备架构又可以细分为冷备架构和温备架构。冷备可以节省一定的能源，但温备能够大大减少手工操作时间，因此一般情况下推荐用温备的方式。
    
    主备架构的有点就是简单，主备机之间不需要进行交互，状态判断和倒换操作由人工执行，系统实现很简单。而缺点正好也体现在“人工操作”这点上。和存储高可用中的主备复制架构类似，计算高可用的主备架构也比较适合于内部管理系统、后台管理系统这类使用人数不多，使用频率不高的业务，不太适合在线的业务。

1. **主从：**和存储高可用的主从复制架构类似，计算高可用的主从架构中的从机也是要执行任务的。任务分配器需要将任务进行分类，确定哪些任务可以发送给主机执行，哪些任务可以发送给备机执行。

    由于主从架构从机也执行任务，发挥了从机的硬件性能。但主从架构需要将任务分类，任务分配器会复杂一些。

1. **对称集群：**主备架构和主从架构通过冗余一台服务器来提升可用性，且需要人工来切换主备或主从。但在可用性要求更严格的场景中，我们需要系统能够自动完成切换操作，这就是高可用集群方案。高可用计算的集群根据集群中服务器节点角色的不同，可以分为两类：一类是对称集群，即集群中每个服务器的角色都是一样的，都可以执行所有任务；一类是非对称集群，集群中的服务器分为多个不同的角色，不同的角色执行不同的任务。而对称集群更通俗的叫法是负载均衡集群。

    负载均衡集群的设计关键点在于两点：任务分配器需要检测服务器状态，任务分配器需要选取分配策略。任务分配策略比较简单，轮询和随机基本就够了；状态检测稍微复杂一些，既要监测服务器的状态，例如服务器是否宕机、网络是否正常等；同时还要检测任务的执行状态，例如任务是否卡死、是否执行时间过长等。常用的做法是任务分配器和服务器之间通过心跳来传递信息，包括服务器信息和任务信息，然后根据实际情况来确定状态判断条件。

1. **非对称集群：**其中不同服务器的角色是不同的，不同角色的服务器承担不同的职责。以Master-Slave为例，部分任务是Master服务器才能执行，部分任务是Slave服务器才能执行。

    其设计复杂度主要体现在：
    1. 任务分配策略更加复杂：需要将任务划分为不同类型并分配给不同角色的集群节点。
    2. 角色分配策略实现比较复杂：可能需要使用Paxos这类复杂的算法来实现Leader的选举。

    例如ZooKeeper，其任务分配器中不存在独立的任务分配器节点，每个Server都是任务分配器，Follower收到请求后会进行判断，如果是写请求就转发给Leader，如果是读请求就自己处理。而角色指定上，其通过ZAB协议来选举Leader，当Leader故障后，所有的Follower节点会暂停读写操作，开始进行选举，直到新的Leader选举出来后才继续对Client提供服务。
    
    如果非对称集群只有两台机器，看起来和存储高可用中的主从倒换方案类似。但在计算高可用非对称集群中，我们并不把这种方案独立出来，因为2台服务器的非对称集群和3台或100台服务器的集群并没有设计上的本质差异。

## 第十章 业务高可用

1. 异地多活
    1. 如果业务期望达到即使在天灾人祸等灾难性故障的情况下，业务也不受影响，或者在几分钟内就能够很快恢复，那么就需要设计异地多活架构。

    2. **异地多活架构的关键点就是异地、多活**，其中异地就是指地理位置上不同的地方，类似于“不要把鸡蛋都放在同一个篮子里”；多活就是指不同地理位置上的系统都能够提供业务服务，这里的“活”是活动、活跃的意思。

    3. 判断一个系统是否符合异地多活的标准：
        1. 正常情况下，用户无论访问哪一个地点的业务系统，都能够得到正确的业务服务。
        2. 某地系统异常情况下，用户访问到其他地方正常的业务系统，也能够得到正确的业务服务。
    4. “备”是备份，正常情况下对外是不提供服务的，如果需要提供服务，则需要大量的人工干预和操作，花费大量的时间才能让“备”变成“活”。
    5. 实现异地多活架构有很高的代价：
        1. 系统复杂度会发生质的变化，需要设计复杂的异地多活架构；
        2. 成本会上升，毕竟要多在一个或多个机房搭建独立的一套业务系统。
    6. 像共享单车、滴滴出行、支付宝、微信这类业务，就需要做异地多活了。这类业务系统中断后，对用户的影响很大。当然，如果业务规模很大，能做异地多活的情况下尽量实现异地多活。
    7. 异地多活架构可以分为同城异区、跨城异地、跨国异地。
        1. 同城异区：这虽然是两个不同地理位置上的机房，但可通过搭建高速网络能够实现和同一个机房内几乎一样的网络传输速度，因此逻辑上可以将他们看作同一个机房，这样的设计大大降低了复杂度，减少了异地多活的设计和实现复杂度及成本。但如果发生了本地区的自然灾害却无能为力。结合复杂度、成本、故障发生概率来综合考虑，同城异区是应对机房级别故障的最优架构。

            其关键在于搭建高速网络将两个机房连接起来，达到近似一个本地机房的效果。架构设计上可以将两个机房当做本地机房来设计，无需额外考虑。

        2. 跨城异地：业务部署在不同城市的多个机房，而且距离最好远一些。跨城异地是为了解决天灾人祸问题的，因此需要在距离上比较远，才能有效应对这类极端灾难事件。

            然而距离增加带来的最主要原因是两个机房的网络传输速度会降低，而且跨城异地肯定会导致数据不一致，因此需要根据数据的特性来做不同的架构。所以支付宝等金融相关的系统，对余额这类数据，不能做跨城异地的多活架构，而只能采用同城异区的这种架构。
            
            而对数据一致性要求不那么高，或者数据不怎么改变，或者即使数据丢失影响也不大的业务，跨城异地多活就能够派上用场了。例如，用户登录(数据不一致用户重新登录即可)、新闻类网站(一天内的新闻数据变化少)、微博类网站(某地丢失用户发布的微博或评论影响不大)这些业务采用跨城异地多活，就能够很好地应对某地区极端灾难的场景。
            
            其关键在于数据不一致的情况下，业务不受影响或影响很小，这从逻辑的角度上来说其实是矛盾的，架构设计的主要目的就是为了解决这个矛盾。
        1. 跨国异地：这种多活的主要应用场景一般是为不同地区用户提供服务以及只读类业务做多活这种情况。对架构设计要求不高。

    1. 异地多活设计技巧
        1. 保证核心业务的异地多活。例：模拟案例中登录为核心业务，而不是要求数据一致性的注册。

        2. 核心数据最终一致性：
            1. 尽量减少异地多活机房的距离，搭建高速网络；
            2. 尽量减少数据同步，只同步核心业务相关数据；
            3. 保证最终一致性，不保证实时一致性：最终一致性就是在介绍CAP理论时提到的BASE理论，即业务不依赖数据同步的实时性，只要数据最终能一致即可。
        3. 采用多种手段同步数据：
          
            虽然MySQL的主备复制、Redis的Cluster功能、Elasticsearch的集群功能等存储系统本身自带了同步功能，但在某些场景下是无法满足我们也无需要的。尤其是异地多机房部署各种各样的异常情况都可能出现，当我们只考虑存储系统本身的同步功能时，就会发现无法做到真正的异地多活。解决方案就是拓展思路，避免只使用存储系统的同步功能，可以将多种手段配合存储系统的同步来使用，甚至可以不采用存储系统的同步方案，改用自己的同步方案。
            
            同步数据方式包括但不限于：消息队列、二次读取、存储系统同步、回源读取、重新生成数据等
            
        1. 只保证绝大部分用户的异地多活。这里承认了业务无法百分百可用，因此为了让用户心里好受，我们可以采取挂公告、事后对用户进行补偿、补充体验等操作进行安抚或补偿。

    1. 异地多活设计步骤
        1. 业务分级：按照一定标准对业务进行分级，挑选核心业务，只为核心业务设计异地多活。常见分级标准：
            1. 访问量大的业务；
            2. 核心业务；
            3. 产生大量收入的业务。
        2. 数据分类：对核心业务相关的数据进一步分析，目的在于识别所有的数据及数据特征，这些数据特征会影响后面的方案设计。常见数据特征分析维度：
            1. 数据量：包括总的数据量和新增、修改、删除的量；
            2. 唯一性：数据是否要求多个异地机房产生的同一类数据必须保证唯一。如果数据要求必须唯一，要么只能一个中心点产生数据，要么需要设计一个数据唯一生成的算法；
            3. 实时性：如果在A机房修改了数据，要求多长时间必须同步到B机房，实时性要求越高，对同步的要求越高，方案越复杂。
            4. 可丢失性：数据是否可以丢失。
            5. 可恢复性：数据丢失后，是否可以通过某种手段进行恢复，如果数据可以恢复，至少说明对业务影响不那么大，这样可以相应降低异地多活架构设计的复杂度。
        3. 数据同步：常见数据同步方案有：
            1. 存储系统同步：最常用也是最简单的同步方式。例如MySQL的主从同步、主主同步。优点是使用简单，缺点是同步方案都是通用的，无法针对业务数据特点做定制化的控制。
            2. 消息队列同步：采用独立消息队列进行数据同步，常见的消息队列有Kafka、ActiveMQ、RocketMQ等。消息队列同步适合无事务性或无时序性要求的数据。例如对于新注册的用户账号，可以采用消息队列同步；而对于用户密码，就不能采用消息队列通不了。
            3. 重复生成：数据不同步到异地机房，每个机房都可以生成数据，这个方案适合于可以重复生成的数据。例如登陆产生的cookie、session数据以及缓存数据等。
        4. 异常处理：异常处理主要是在问题发生时，避免少量数据异常导致整体业务不可用；问题解决后，将异常的数据进行修正；对用户进行安抚，弥补用户损失。常见的异常处理措施有：
            1. 多通道同步：采取多种方式进行数据同步，可以应对同步通道出故障的情况。例如针对用户账号数据同步可有MySQL主从同步和消息队列同步两种方式。方案设计关键点有：
                1. 一般情况采取两通道即可；
                2. 数据库同步通道和消息队列同步通道不能采用相同的网络连接，可以一个走公网，一个走内网。
                3. 数据是可以重复覆盖的。例如新建账号数据符合此标准，但密码数据不符合此标准。
            2. 同步和访问结合：异地机房通过系统的接口来进行数据访问。方案设计关键点有：
                1. 数据库同步通道和接口访问通道不能采用相同的网络连接，可以接口走公网，数据库同步走内网。
                2. 数据有路由规则，可以根据数据来推断应该访问哪个机房的接口来读取数据。
                3. 由于有同步通道，优先读取本地数据，本地数据无法读取到再通过接口去访问，这样可以大大降低跨机房的异地接口访问数量，适合于实时性要求非常高的数据。
            3. 日志记录：常见日志保存方式有：
                1. 服务器上保存日志，数据库中保存数据，这种方式可以应对单台数据库服务器故障或宕机的情况。
                2. 本地独立系统保存日志，这种方式可以应对某业务服务器和数据库同时宕机的情况。例如，服务器和数据库部署在同一个机架，或者同一个电源线路上，就会出现服务器和数据库同时宕机的情况。
                3. 日志异地保存，这种方式也可以应对机房宕机的情况。
            4. 用户补偿：例如《炉石传说》2017年回档故障，暴雪给予每个用户大约价值人民币200元的补偿（25卡包）。

1. 接口级故障应对方案

    接口级故障的典型表现：系统并没有宕机，网络没有中断，但业务出现问题。例如业务响应缓慢、大量访问超时、大量访问出现异常（弹窗“无法连接数据库”），这类问题的主要原因在于系统压力太大，负载太高，导致无法快速处理业务请求，由此引发更多的后续问题。

    导致接口级故障的原因：
    1. 内部原因：程序bug导致死循环、某个接口导致数据库慢查询、程序逻辑不完善导致耗尽内存等。
    2. 外部原因：黑客攻击、促销或抢购引入了超出平时几倍甚至几十倍的用户、第三方系统大量请求、第三方系统响应缓慢等。

    解决接口级故障的核心思想和异地多活基本类似：**优先保证核心业务，优先保证绝大部分用户**。
    
    具体措施：
    1. 降级：核心思想是丢车保帅，优先保证核心业务：
        1. 系统后门降级：系统预留后门进行降级操作。具体降级指令通过URL参数传入，有一定安全隐患，徐艳在URL中加入密码这类安全措施。其实现成本比较低，但若服务器数量多，需要一台台操作，效率较低。
        2. 独立降级系统：将降级操作独立到一个单独的系统中，可以实现复杂的权限管理、批量操作等功能。
    2. 熔断：应对依赖的外部系统故障。实现的关键是需要有一个统一的API调用层，由API调用层来进行采样或统计；另外还有阈值的设计，实践中一般先根据分析确定阈值，然后上线观察效果，最后进行调优。
    3. 限流：从用户访问压力的角度考虑如何应对故障。只允许系统能够承受的访问量进来，超出系统访问能力的请求将被丢弃。限流一般都是系统内实现的。常见的限流方式分类：
        1. 基于请求限流：从外部访问的请求角度考虑限流，方式有：
            1. 限制总量：限制某个指标的累计上限，常见是限制当前系统服务的用户总量。
            2. 限制时间量：限制一段时间内某个指标的上线。

            这种方式实践中比较难以找到合适的阈值。即使找到合适阈值，其还面临硬件相关问题，例如64核及其对比32核及其业务处理性能并不是2倍，可能是1.5甚至1.1倍。

            为了找到合理阈值，通常可以采用性能压测来确定阈值，但仍存在覆盖场景有限的问题。另外还可以进行逐步优化，即先设定一个阈值然后上线观察运行情况，发现不合理就调整阈值。
        
        2. 基于资源限流：常见的内部资源有连接数、文件句柄、线程数、请求队列等。也可以根据CPU的负载或占用率进行限流，当CPU的占用率超过80%的时候就开始拒绝新的请求。

            其实践中设计面临两个主要难点：如何确定关键资源和如何确定关键资源的阈值，但这也是一个逐步调优的过程。

    1. 排队：实际上是限流的一个变种。排队虽然没有直接拒绝用户，但用户等了很长时间后进入系统，体验并不一定比限流好。

        由于排队需要临时缓存大量的业务请求，单个系统内部无法缓存这么多数据，一般情况下，排队需要用独立的系统去实现。例如，使用Kafka这类消息队列来缓存用户请求。

## 其他相关摘要

1. 存储高可用数据集群中涉及到的两个算法：
    1. 分布式事务算法：其主要目的是为了保证分散在多个节点上的数据统一提交或回滚。有二阶段提交(Two-phase commit protocol, 2PC)、三阶段提交(Three-phase commit protocol, 3PC)两个著名主流算法。
        1. 二阶段提交(Two-phase commit protocol, 2PC)

            二阶段提交算法主要由两个阶段组成：commit请求阶段、commit提交阶段。其成立基于以下假设：
            1. 在分布式系统中，存在一个节点作为协调者(Coordinator)，其他节点作为参与者(Cohorts)，且节点之间可以进行网络通信。
            2. 所有节点都采用预写式日志，且日志被写入后即保持在可靠的存储设备上，即使节点损坏，也不会导致日志数据的消失。
            3. 所有节点不会永久性损坏，即使损坏，仍然可以恢复。
        
            2PC是强一致性算法，优点是实现简单，缺点有同步阻塞，即协调者与参与者互相等待对方的响应消息，等待过程中节点处于阻塞状态，所以极端情况下无论怎么处理都可能出现状态不一致的情况；另外还有单点故障，即协调者是整个算法的单点，如果协调者故障，则参与者会一直阻塞下去。
        
        3. 三阶段提交(Three-phase commit protocol, 3PC)

            三阶段提交算法是针对二阶段提交算法在“单点故障”产生的问题所提出的解决方案。通过在二阶段提交算法中的第一阶段和第二阶段之间插入一个新的阶段“准备阶段”，当协调者故障后，参与者可以通过超时提交来避免一直阻塞。

            三阶段提交算法虽然避免了二阶段提交算法的协调者单点故障导致系统阻塞的问题，但同样存在数据不一致问题。

    1. 分布式一致性算法：主要目的是为了保证同一份数据在多个节点上的一致性，以满足CAP中的CP要求。

        复制状态机是实现分布式一致性的常用技术，其主要角色有：副本、状态机、算法。复制状态机的核心就是分布式一致性算法，其都比较复杂。
        
        1. Paxos

            Paxos是被理论上证明为正确的算法（另外一个被数学家理论上证明的定理是CAP理论），但Paxos存在着理论特别复杂难以理解、确实很多细节难以实现的明显问题。
            
            因此在工程实践中Paxos算法一般步骤是：
            1. 以Paxos算法为基础，开始尝试实现。
            2. 发现有的地方很难实现，或者没有明确规定如何实现，于是想了一个方案去实施。
            3. 最终看起来完成了Paxos算法，但算法的正确性已经无法完全保证，可能在某些场合下算法达不到一致性的目的。
        
            理解Paxos算法有几个关键点：
            
            1. Paxos算法是多数一致性(Quorum)，不是全体一致性。
            2. Client发起的请求可以是任何操作，而不仅限于写操作，读操作也可以。
            3. Paxos算法中的角色(Proposer、Acceptor、Learner、Leader)是逻辑上的划分，不是集群上的物理节点要这样划分，事实上实现的时候比较灵活。
        
        2. Raft

            Raft算法通过将分布式一致性问题拆分为3个子问题来简化算法：Leader选举、日志复制、安全保证；Raft强化了Leader的作用，通过Leader来保证分布式一致性。
        
        3. ZAB
        
            ZAB全称是ZooKeeper Atomic Broadcast Protocol，是ZooKeeper系统中采用的分布式一致性算法。抛开各种实现细节，ZAB的实现和Raft其实是相似 ，例如，强化Leader的作用，通过Leader来保证分布式一致性。ZAB肯定是Paxos的一个不完整版实现。

            ZAB同Paxos和Raft有一个交大的差异是复制方式，Paxos和Raft采用的是State Machine Replication (又称为Active Replication)，ZAB采用的是Primary Backup (又称为Passive Replication)，两种实现方式差异前者为各个节点复制的是具体的操作，然后每个节点自己执行操作；后者为Leader节点执行操作，将执行结果复制给其他节点。
