07 高可用存储架构

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

对任何一个高可用存储方案,从以下几个方面分析:

  • 数据如何复制?
  • 各个节点的职责是什么?
  • 如何应对复制延迟?
  • 如何应对复制中断?

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

常见的双机高可用架构为:主备、主从、主备/主从切换和主主。

0.1. 主备复制

最常见、最简单的一种存储高可用方案,几乎所有的存储系统都提供了主备复制的功能,例如 MySQLRedisMongoDB 等。

主备架构中的“备机”主要起到备份作用,并不承担实际的业务读写操作,如果要把备机改为主机,需要人工操作。

0.1.1. 优点

主备复制架构的优点就是简单,表现有:

  • 对于客户端来说,不需要感知备机的存在,即使灾难恢复后,原来的备机被人工修改为主机后,对于客户端来说,只是认为主机的地址换了而已,无须知道是原来的备机升级为主机。
  • 对于主机和备机来说,双方只需要进行数据复制即可,无须进行状态判断和主备切换这类复杂的操作。

0.1.2. 缺点

  • 备机只做备份,不提供读写,硬件浪费。
  • 故障后需要人工干预(效率低下且容易出错),无法自动恢复。

一般系统的数据变更频率低,即使在某些场景下丢失数据,也可以通过人工的方式补全。这样可以采用主备架构。

0.2. 主从复制

主从复制和主备复制只有一字之差,“从”意思是“随从”(需要干活,提供读取功能),“备”的意思是备份。也就是说,主机负责读写操作,从机只负责读操作,不负责写操作。

0.2.1. 优点

主从复制与主备复制相比,优点有:

  • 主从复制在主机故障时,读操作相关的业务可以继续运行。
  • 主从复制架构的从机提供读操作,发挥了硬件的性能。

0.2.2. 缺点

  • 主从复制架构中,客户端需要感知主从关系,并将不同的操作发给不同的机器进行处理,复杂度比主备复制要高。
  • 主从复制架构中,从机提供读业务,如果主从复制延迟比较大,业务会因为数据不一致出现问题。
  • 故障时需要人工干预。

一般情况下,写少读多的业务使用主从复制的存储架构比较多。

0.3. 双机切换

主备复制和主从复制方案存在两个共性的问题:

  • 主机故障后,无法进行写操作。
  • 如果主机无法恢复,需要人工指定新的主机角色。

双机切换就是为了解决这两个问题而产生的,包括主备切换和主从切换两种方案。

在原有方案的基础上增加“切换”功能,即系统自动决定主机角色,并完成角色切换(主备切换和主从切换在切换的设计上没有差别)。

要实现一个完善的切换方案,必须考虑这几个关键的设计点:

  1. 主备间状态判断:
    1. 状态传递的渠道:相互连接/第三方仲裁
    2. 状态检测的内容:机器掉电/进程状态/响应状态
  2. 切换决策:
    1. 切换时机:备机升级为主机的判断标准(机器掉电/主进程退出/响应超时)
    2. 切换策略:原主机故障恢复后,重新升级为主机还是继续做备机
    3. 自动程度:全自动切换/人工判断后切换
  3. 数据冲突解决:原主机故障恢复后,主备之间数据冲突

0.3.1. 常见架构

0.3.1.1. 互连式

主备机直接建立状态传递的渠道。

在主备复制的架构基础上,主机和备机多了一个“状态传递”的通道,这个通道就是用来传递状态信息的。

这个通道的具体实现可以有很多方式:

  1. 网络连接(例如,各开一个端口)或者非网络连接(用串口线连接)。
  2. 主机发送状态给备机,或者备机到主机来获取状态信息。
  3. 与数据复制通道共用,或者独立一条通道。
  4. 状态传递通道可以是一条或多条,还可以是不同类型的通道混合(例如,网络 + 串口)。

为了充分利用切换方案能够自动决定主机这个优势,客户端会有一些相应的改变,常见的方式有:

  • 为了切换后不影响客户端的访问,主机和备机之间共享一个对客户端来说唯一的地址。例如虚拟 IP,主机需要绑定这个虚拟的 IP。
  • 客户端同时记录主备机的地址,哪个能访问就访问哪个;备机虽然能收到客户端的操作请求,但是会直接拒绝,拒绝的原因就是“备机不对外提供服务”。

互连式主备切换的缺点

  • 如果状态传递的通道本身有故障(例如,网线断了),那么备机也会认为主机故障了从而将自己升级为主机,而此时主机并没有故障,最终就可能出现两个主机。
  • 通过增加多个通道来增强状态传递的可靠性,但这样只降低了通道故障概率,不能从根本上解决这个缺点,而且通道越多状态决策越复杂,对备机来说,可能从不同的通道收到不同甚至矛盾的状态信息。

0.3.1.2. 中介式

中介式指的是在主备两者之外引入第三方中介,主备机之间不直接连接,而都去连接中介,并且通过中介来传递状态信息。

虽然引入了第三方中介,但是在状态传递和决策上更简单:

  1. 连接管理更简单:主备机无须建立和管理多种类型的状态传递连接通道,只要连接到中介即可,降低了主备机的连接管理复杂度。
  2. 状态决策更简单:主备机无须考虑多种类型的连接通道获取的状态信息如何决策的问题,只需要按照如下算法完成状态决策。
    1. 初始状态都为备机,且只要与中介断开连接,就将自己降级为备机,可能出现双备机的情况。
    2. 主机与中介断连后,中介能够立刻告知备机,备机将自己升级为主机。
    3. 旧主机恢复后以新备机身份向中介上报自己的状态。
    4. 掉电重启或进程重启后,旧主机初始状态为备机,与中介恢复连接后,保持备机状态不变。
    5. 主备机与中介连接都正常时,按照实际的状态决定(如主机响应超时)是否进行切换。

中介式架构在状态传递和状态决策上更加简单,其关键代价就在于如何实现中介本身的高可用

如果中介自己宕机了,整个系统就进入了双备的状态,写操作相关的业务就不可用了。这就陷入了一个递归陷阱:为了实现高可用,我们引入中介,但中介本身又要求高可用,于是又要设计中介的高可用方案……如此递归下去就无穷无尽了。

开源方案有比较成熟的中介式解决方案,例如 ZooKeeperKeepalived

ZooKeeper 本身已经实现了高可用集群架构,因此已经解决了中介本身的可靠性问题,在工程实践中推荐基于 ZooKeeper 搭建中介式切换架构。

0.3.1.3. 模拟式

主备机之间不传递任何状态数据,备机模拟客户端,向主机发起模拟的读写操作,根据响应情况来判断主机的状态。

对比互连式切换架构,主备机之间只有数据复制通道,而没有状态传递通道,备机通过模拟的读写操作来探测主机的状态,然后根据读写操作的响应情况来进行状态决策。

模拟式切换与互连式切换相比,优点是实现更加简单,省去状态传递通道的建立和管理工作。

简单既是优点,同时也是缺点。因为模拟式读写操作获取的状态信息只有响应信息(例如,HTTP 404,超时、响应时间超过 3 秒等),没有互连式那样多样(除了响应信息,还可以包含 CPU 负载、I/O 负载、吞吐量、响应时间等),基于有限的状态来做状态决策,可能出现偏差。

0.4. 主主复制

两台机器都是主机,互相将数据复制给对方,客户端可以任意挑选其中一台机器进行读写操作。

相比主备切换架构,主主复制架构具有如下特点:

  • 两台都是主机,不存在切换的概念。
  • 客户端无须区分不同角色的主机,随便将读写操作发送给哪台主机都可以。

主主复制架构从总体上来看要简单很多,无须状态信息传递,也无须状态决策和状态切换。

主主复制架构有其独特的复杂性,主主复制架构,必须保证数据能够双向复制,而很多数据(用户账户、库存数据、余额数据)是不能双向复制的。

因此,主主复制架构对数据的设计有严格的要求,一般适合于那些临时性、可丢失、可覆盖的数据场景。例如:

  • 用户登录产生的 session 数据(可以重新登录生成)
  • 用户行为的日志数据(可以丢失)
  • 论坛的草稿数据(可以丢失)

0.5. 数据集群

主备、主从、主主架构本质上都有一个隐含的假设:主机能够存储所有数据,但主机本身的存储和处理能力肯定是有极限的,必须使用多台服务器来存储数据,这就是数据集群架构。

集群是多台(数量上至少是 3 台)机器组合在一起形成一个统一的系统;相比而言,主备、主从都是 2 台机器。

根据集群中机器承担的不同角色来划分,集群可以分为两类:

  • 数据集中集群
  • 数据分散集群

0.5.1. 数据集中集群

数据集中集群与主备、主从类架构相似,也称为 1 主多备或者 1 主多从。无论是 1 主 1 从、1 主 1 备,还是 1 主多备、1 主多从,数据都只能往主机中写,而读操作可以参考主备、主从架构进行灵活多变。

由于集群里面的服务器数量更多,导致复杂度整体更高一些,具体体现在:

  1. 主机如何将数据复制给备机:主备和主从架构中,只有一条复制通道,而数据集中集群架构中,存在多条复制通道。
    1. 首先会增大主机复制的压力,某些场景下需要考虑如何降低主机复制压力,或者降低主机复制给正常读写带来的压力。
    2. 其次,多条复制通道可能会导致多个备机之间数据不一致,某些场景下需要对备机之间的数据一致性进行检查和修正。
  2. 备机如何检测主机状态:主备和主从架构中,只有一台备机需要进行主机状态判断。在数据集中集群架构中,多台备机都需要对主机状态进行判断,而不同的备机判断的结果可能是不同的,如何处理不同备机对主机状态的不同判断,是一个复杂的问题。
  3. 主机故障后,如何决定新的主机:主从架构中,如果主机故障,将备机升级为主机即可;而在数据集中集群架构中,如何从多台备机中协商选择一台升级为主机,这也是一个复杂的问题。

目前开源的数据集中集群以 ZooKeeper 为典型,ZooKeeper 通过 ZAB 算法来解决上述提到的几个问题,但 ZAB 算法的复杂度是很高的。

0.5.2. 数据分散集群

多个服务器组成一个集群,每台服务器都会负责存储一部分数据;同时,为了提升硬件利用率,每台服务器又会备份一部分数据

数据分散集群的复杂点在于如何将数据分配到不同的服务器上,算法需要考虑这些设计点:

  1. 均衡性:保证服务器上的数据分区基本是均衡的,不能存在某台服务器上的分区数量是另外一台服务器的几倍的情况。
  2. 容错性:当出现部分服务器故障时,需要将原来分配给故障服务器的数据分区重新分配给其他服务器。
  3. 可伸缩性:当集群容量不够,扩充新的服务器后,能够自动将部分数据分区迁移到新服务器,并保证扩容后所有服务器的均衡性。

数据分散集群和数据集中集群的不同点在于:

  • 数据分散集群中,每台服务器都可以处理读写请求,因此不存在数据集中集群中负责写的主机那样的角色。
  • 数据分散集群中,必须有一个角色来负责执行数据分配算法,这个角色可以是独立的一台服务器,也可以是集群自己选举出的一台服务器。如果是集群服务器选举出来一台机器承担数据分区分配的职责,则这台服务器一般也会叫作主机,但这里的“主机”和数据集中集群中的“主机”,其职责是有差异的。

Hadoop 的实现就是独立的服务器(Namenode)负责数据分区的分配。

与 Hadoop 不同的是,Elasticsearch 集群通过选举一台服务器来做数据分区的分配,叫作 master node。

  • 数据集中集群架构中,客户端只能将数据写到主机;
  • 数据分散集群架构中,客户端向任意服务器读写数据。

正是因为这个关键的差异,决定了两种集群的应用场景不同。

  • 数据集中集群适合数据量不大,集群机器数量不多的场景。例如,ZooKeeper 集群,一般推荐 5 台机器左右,数据量是单台服务器就能够支撑;
  • 数据分散集群由于良好的可伸缩性,适合业务数据量巨大集群机器数量庞大的业务场景。例如,Hadoop 集群、HBase 集群,大规模的集群可以达到上百台甚至上千台服务器

0.6. 数据分区

上述存储高可用架构都是基于硬件故障的场景去考虑和设计的,主要考虑当部分硬件可能损坏的情况下系统应该如何处理,但对于一些影响非常大的灾难或者事故来说,有可能所有的硬件全部故障,这种情况下基于硬件故障而设计的高可用架构不再适用,需要基于地理级别的故障来设计高可用架构,这就是数据分区架构产生的背景。

将数据按照一定的规则进行分区,不同分区分布在不同的地理位置上,每个分区存储一部分数据,通过这种方式来规避地理级别的故障所造成的巨大影响。

采用数据分区架构后,即使某个地区发生严重的自然灾害或者事故,受影响的也只是一部分数据,而不是全部数据都不可用;当故障恢复后,其他地区备份的数据也可以帮助故障地区快速恢复业务。

设计一个良好的数据分区架构,需要从多方面去考虑:

  1. 数据量:数据量的大小直接决定了分区的规则复杂度
  2. 分区规则:地理位置有近有远,因此可以得到不同的分区规则,包括洲际分区、国家分区、城市分区。具体采取哪种或者哪几种规则,需要综合考虑业务范围、成本等因素。
  3. 复制规则:每个分区本身的数据量虽然只是整体数据的一部分,但还是很大,这部分数据如果损坏或者丢失,损失同样难以接受。因此即使是分区架构,同样需要考虑复制方案。常见的分区复制规则有三种:集中式、互备式和独立式。

0.6.1. 集中式

集中式备份指存在一个总的备份中心,所有的分区都将数据备份到备份中心,集中式备份架构的优缺点是:

  • 设计简单,各分区之间并无直接联系,可以做到互不影响。
  • 扩展容易,如果要增加第新的分区,只需要将新分区的数据复制到备份中心即可,其他分区不受影响。
  • 成本较高,需要建设一个独立的备份中心。

0.6.2. 互备式

互备式备份指每个分区备份另外一个分区的数据,互备式备份架构的优缺点是:

  • 设计比较复杂,各个分区除了要承担业务数据存储,还需要承担备份功能,相互之间互相关联和影响。
  • 扩展麻烦,如果增加一个新分区,则需要修改依赖和被依赖的两个分区的备份关系。
  • 成本低,直接利用已有的设备。

0.6.3. 独立式

独立式备份指每个分区自己有独立的备份中心。

注意,各个分区的备份并不和原来的分区在一个地方,这样做的主要目的是规避同城或者相同地理位置同时发生灾难性故障的极端情况

独立式备份架构的优缺点是:

  • 设计简单,各分区互不影响。
  • 扩展容易,新增加的分区只需要搭建自己的备份中心即可。
  • 成本高,每个分区需要独立的备份中心,备份中心的场地成本是主要成本,因此独立式比集中式成本要高很多。
上次修改: 8 June 2020