架构师修炼系列【存储高可用】

存储高可用方案的本质都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题 。 因此,对任何 一个高可用存储方案,我们需要从以下几个方面去进行思考和分析:

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

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

主备复制

主备复制是最常见也是最简单的一种存储高可用方案,几乎所有的存储系统都提供了主备复制的功能,例如, MySQL 、 Redis 、 MongoDB等
在这里插入图片描述
主备方案详细设计思想:

  • 主机存储数据,通过复制通道将数据复制到备机
  • 正常情况下 , 客户端无论读写操作 ,都发送给主机,备机不对外提供任何读写服务
  • 主机故障情况下(主机岩机),客户端不会自动将请求发给备机,此时整个系统处于不可用状态,不能读写数据,但数据并没有全部丢失,因为备机上有数据
  • 如果主机能够恢复(不管是人工恢复还是自动恢复),客户端继续访问主机,主机继续将数据复制给备机
  • 如果主机不能恢复(机器硬盘损坏,短时间内无法恢复), 则 需要人工操作,将备机升为主机,然后让客户端访问新的主机(原来的备机);同时,为了继续保持主备架构, 需要人工增加新的机器作为备机
  • 主机不能恢复的情况下,成功写入了主机但还没有复制到备机的数据会丢失, 需要人工进行排查和恢复, 也许有 的数据就永远丢失了,业务上需要考虑如何应对此类风险
  • 如果主备间数据复制延迟,由于备机并不对外提供读写操作,因此对业务没有影响, 但如果延迟较多,恰好此时主机又岩机了, 则可能丢失较多数据 ,因此对于复制延迟也不能掉以轻心。 一般的做法是做复制延迟的监控措施,当延迟的数据量较大时及时报警 ,由人工干预处理

通过上面的描述我们可以看到,主备架构中的“备机”主要还是起到一个备份作用,并不承担实际的业务读写操作

优缺点

  • 对于客户端来说,不需要感知备机的存在,即使灾难恢复后,原来的备机被人工修改为主机后,对于客户端来说,只是认为主机的地址换 了而己,无须知道是原来 的 备机升级 为主机了
  • 对于主机和备机来说 , 双方只需要进行数据复制即可,无须进行状态判断和主备倒换这 类复杂的操作
  • 备机仅仅只为备份,并没有提供读写操作 , 硬件成本上有浪费
  • 故障后需要人工干预,无法自动恢复

综合主备复制架构的优缺点,内部的后台管理系统使用主备复制架构的情况会 比较多。例 如,学生管理系统、员工管理系统、假期管理系统,等等。因为这类系统的数据变更频率很低, 即使在某些场景下丢失,也可以通过人工的方式补全

主从复制

主从复制和主备复制只有一字之差,“备”的意思是备份,“从”意思是“随从、 仆从”。我 们可以理解为仆从是要帮主人干活的,这里的干活就是承担“读”的操作 。 也就是说, 主机负责读写操作,从机只负责读操作,不负责写操作
在这里插入图片描述
主从方案详细设计思想:

  • 主机存储数据,通过复制通道将数据复制到从机
  • 正常情况下,客户端写操作发送给主机,读操作可发送给主机也可以发送给从机,具 体如何选择,可以根据业务的特点选择 。 可以随机读,可以轮询读,可以只读主机, 等等
  • 主机故障情况下(主机岩机),客户端无法进行写操作,但可以将读操作发送给从机,从机继续响应读操作,此时和写操作相关的业务不可用(例如,论坛发帖),但和读操作相关的操作不受影响(如,论坛看帖)
  • 如果主机能够恢复(不管是人工恢复还是自动恢复),客户端继续将写操作请求发送 给主机,主机继续将数据复制给备机
  • 如果主机不能恢复(例如,机器硬盘损坏,短时间内无法恢复),则需要人工操作,将备机升为主机,然后让客户端访问新的主机(即原来的备机);同时,为了继续保持主备架构, 需要人工增加新的机器作为备机
  • 主机不能恢复的情况下,成功写入了主机但还没有复制到备机的数据会丢失,需要人工进行排查和恢复,也许有的数据就永远丢失了,业务上需要考虑如何应对此类风险
  • 如果主从间数据复制延迟,则会出现主从读取的数据不一致的问题。例如,用户刚发 了 一个新帖,此时数据还没有从主机复制到从机,用户刷新了页面,这个读操作请求发送到了从机,从机上并没有用户最新发表的帖子,这时用户就看不到刚才发的帖子了,会以为帖子丢 了;如果再刷新一次,可能又展现出来了,因为第二次刷新的读请求发给了主机
  • 如果主从间延迟较多,恰好此时主机又看机了,则可能丢失较多数据,因此对于复制延迟也不能掉以轻心,一般的做法是做复制延迟的监控措施,当延迟的数据量较大时及时报警 , 由人工干预处理

优缺点

  • 主从复制在主机故障时,读操作相关的业务不受影响.
  • 主从复制架构的从机提供读操作,发挥了硬件的性能
  • 主从复制要比主备复制复杂更多,主要体现在客户端需要感知主从关系,并将不同的操 作发给不同的机器进行处理

主从复制同样具备和主备复制一样的缺点:故障时需要人工干预。 人工处理的效率是很低的,人工在执行恢复操作的过程中也容易出锚,因为这类操作并不常见

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

主备倒换与主从倒换

关键思想

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

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

主备倒换和主从倒换方案就是为了解决上述两个问题而产生的。简单来说,这两个方案就是在原有方案的基础上增加 “倒换”功能, 即系统自动决定主机角色, 并完成角色切换 。主备倒换和主从倒换在倒换的设计上没有差别

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

  • 主备间状态判断,主要包括两方面:状态传递的渠道和状态检测的内容
    • 状态传递的渠道。是相互间互相连接,还是第三方仲裁?
    • 状态检测的内容。例如,机器是否掉电,进程是否存在,响应是否缓慢,等等
  • 倒换决策:主要包括几方面 : 倒换时机、倒换策略、自动程度
    • 倒换时机,什么情况下备机应该升级为主机? 是机器掉电后备机才升级,还是主机上的进程不存在就升级,还是主机响应时间超过2s就升级,还是3分钟内主机连续重启3次就升级,等等。
    • 倒换策略,原来的主机故障恢复后 , 要再次倒换,确保原来的主机继续做主机,还是原来的主机故 障恢复后自动成为新的备机
    • 自动程度,倒换是完全自动的,还是半自动的?例如,系统判断当前需要倒换,但需要人工做最终 的确认操作(例如,单击一下“倒换”按钮)
  • 数据冲突解决,当原有故障的主机恢复后,新旧主机之间可能存在数据冲突。例如,用户在旧主机上新增了一条 ID 为 100 的数据,这个数据还没有复制到旧的备机,此时发生了倒换,旧的备机升级为新的主机,用户又在新的主机上新增了一条ID为100 的数据,当旧的故障主机恢复后,这两条ID都为100 的数据

以上设计点并没有放之四海而皆准的答案,不同的业务要求不一样,所以倒换方案比复制方案不只是多了 一个倒换功能那么简单,而是复杂度上升了一个量级

常见架构

根据状态传递渠道的不同,常见的主备倒换架构有三种形式:互连式、中介式和模拟式

互连式

互连式就是指主备机直接建立状态传递的渠道,架构如下图所示(与主备复制架构对比)

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

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

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

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

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

互连式主备倒换主要的缺点在于:如果状态传递的通道本身有故障,那么备机也会认为主机故障了从而将自己升级为主机 ,而此时主机并没有故障,最终就可能出现两个主机。虽然可以通过增加多个通道来增强状态传递的可靠性,但这样做只是降低了通道故障概率而己,不能从根本上解决这个缺点。而且通道越多,后续的状态决策会更加复杂。因为对备机来说,可能从不同的通道收到了不同甚至矛盾的状态信息

中介式

中介式指的是在主备两者之外引入第三方中介,主备机之间不直接连接,而都去连接中介,并且通过中介来传递状态信息,其架构图如下:
在这里插入图片描述
对比一下互连式倒换架构,不难发现,主机和备机不再通过互联通道传递状态信息, 而是都将状态上报给中介这一角色。单纯从架构上看,中介式似乎比互连式更加复杂了 。首先要引入中介,然后要各自上报状态,然而事实上,中介式架构在状态传递和决策上却更加简单了

  • 连接管理更简单:主备机无须再建立和管理多种类型的状态传递连接通道,只要连接到中介即可,实际上是降低了主备机的连接管理复杂度,例如,互连式要求主机开一个监听端口,备机来获取状态信息;或者要求备机开一个监昕 端口,主机推送状态信息到备机:如果还采用了串口连接,则需要增加串口连接管理和数据读取 。采用中介式后, 主备机都只需要把状态信息发送给中介,或者从中介获取对方的状态信息,无论发迭,还是获取, 主备机都是作为中介的客户端去操作,复杂度会降低很多
  • 状态决策更简单:主备机的状态决策简单了,无须考虑多种类型的连接通道获取的状态信息如何决策的问题, 只需要按照如下简单的算法即可完成状态决策
    • 无论主机,还是备机,初始状态都是备机,并且只要与中介断开连接,就将自己降级为备机,因此可能出现双备机的情况
    • 主机与中介断连后,中介能够立刻告知备机,备机将自己升级为主机
    • 如果是网络中断导致主机与中介断连,主机自己会降级为备机,网络恢复后 ,旧的主机以新的备机身份向中介上报自己的状态
    • 如果是掉电重启或进程重启,旧的主机初始状态为备机,与中介恢复连接后,发现已经有主机了,保持自己备机状态不变
    • 主备机与中介连接都正常的情况下 ,按照实际的状态决定是否进行倒换 。例 如, 主机响应时间超过 3s 就进行倒换,主机阵级为备机,备机升级为主机即可

虽然中介式架构在状态传递和状态决策上更加简单,但并不意味着这种优点是没有代价的,其关键代价就在于如何实现中介本身的高可用。如果中介自己岩机了,整个系统就进入了双备的状态,写操作相关的业务就不可用了 。这就陷入了 一个递归的陷阱:为了实现高可用,我们引入中介,但中介本身又要求高可用,于是又要设计中介的高可用方案……如此递归下去就无 穷无尽了

MongoDB的Replica Set 采取的就是这种方式 ,其基本架构如下图所示
在这里插入图片描述

  • MongoDB(M)表示主节点, MongoDB(S)表示备节点, MongoDB(A)表示仲裁节点 。 主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点
  • 默认设置下,主节点提供所有增删查改服务,备节点不提供任何服务,但是可以通过设置使备节点提供查询服务,这样就可以减少主节点的压力,当客户端进行数据查询时,请求自动转到备节点上,这个设置叫作 Read Preference Modes ,同时Java客户端提供了简单的配置方式,不必直接对数据库进行操作
  • 仲裁节点是一种特殊的节点,它本身并不存储数据,主要的作用是决定哪一个备节点在主节点挂掉之后提升为主节点,所以客户端不需要连接此节点,这里虽然只有一个备节点,但是仍然需要一个仲裁节点来提升备节点级别

开源方案己经有很成熟的解决方案,那就是ZooKeeper,主要用来解决分布式应用中经常遇到的一些数据管理问题。例如,统 一命名服务、状态同步服务、集群管理、分布式应用配置项的管理,等等。 ZooKeeper本身是一个高可用的系统,在高可用架构中有很多用途,主备倒换中的中介就可以用 ZooKeeper 来做 状态同步 。

模拟式

模拟式指主备机之间并不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模
拟的读写操作,根据读写操作的响应情况来判断主机的状态 。 其基本架构如下图所示 。
在这里插入图片描述
对比一下互连式倒换架构,不难发现,主备机之间只有数据复制通道,而没有状态传递通道,备机通过模拟的读写操作来探测主机的状态,然后根据读写操作的响应情况来进行状态决策

模拟式倒换与互连式倒换相 比 ,具有如下优缺点 :

  • 实现更加简单 , 因为省去 了 状态传递通道的 建立和管理工作
  • 模拟式读写操作获取的状态信息只有响应信息(例如, HTTP404,超时、响应时间超过3s等),没有互连式那样多样(除了响应信息, 还可以包含CPU负载 、I/ O负载 、吞吐量 、响应时间等),基于有限的状态来做状态决策,可能出现偏差

主主复制

主主复制指的是两台机器都是主机,互相将数据复制给对方,客户端可以任意挑选其中一台机器进行读写操作 , 其基本架构如下图所示
在这里插入图片描述
主主复制架构思想详细解释如下:

  • 两台主机都存储数据,通过复制通道将数据复制到另外一 台主机
  • 正常情况下,客户端可以将读写操作发送给任意一 台主机
  • 一台主机故障情况下,例如主机 A 岩机,客户端只需要将读写操作发送给主机B即可 ,反之亦然
  • 如果故障的主机 A 能够恢复(不管是人工恢复还是自动恢复), 则客户端继续访问两台主机,两 台主机间继续互相复制对方数据
  • 如果故障的主机 A 不能恢复(例如 ,机器硬盘损坏 , 短时间内无法恢复),则需要人工操作,增加一台新的机器作为主机
  • 原有故障主机 A 不能恢复的情况下,成功写入了原有故障主机但还没有复制到正常主机B的数据会丢失,需要人工进行排查和恢复, 也许有的数据就永远丢失了,业务上需要考虑如何应对此类风险
  • 如果两台主机间复制延迟,则可能出现客户端刚写入了数据到主机 A,然后到主机B去读取 ,此时读取不到刚刚写入的数据

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

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

从上面的描述来看,主主复制架构从总体上来看要简单很多,无须状态信息传递,也无须状态决策和状态切换。然而事实上主主复制架构也并不简单,而是有其独特的复杂性,具体表现在:如果采取主主复制架构,必须保证数据能够双向复制,而很多数据是不能双向复制的

  • 例如:用户注册后生成的用户ID ,如果按照数字增长,那就不能双向复制,否则就会出现X用户在主机A注册,分配的用户ID是100 ,同时Y用户在主机B注册,分配的用户ID也是100 ,这就出现了冲突
  • 例如:库存不能双向复制,一件商品库存100件,主机A上减了1件变成99,主机B上减了2件变成98,然后主机A将库存99复制到主机B,主机B原有的库存98被覆盖,变成了99,而实际上此时真正的库存是 97

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

数据集群

主备、主从 、 主主架构本质上都有一个隐含的假设:主机能够存储所有数据 ,但主机本身的存储和处理能力肯定是有极限的,我们必须使用多台服务器来存储如此大量的数据,这就是数据集群架构
简单来说,集群就是多台机器组合在一起形成一个统一的系统,这里的多台数量上至少是3台,相比而言,主备、主从都是2台机器,根据集群中机器承担的不同角色来划分, 集群可以分为两类:数据集中集群、数据分散集群

数据集中集群

数据集中集群与主备、主从这类架构相似,我们也可以称数据集中集群为1主多备或1主多从,无论1主1从、1主1备,还是1主多备、1主多从,数据都只能往主机中写,而读操作可以参考主备、主从架构进行灵活多变。下图是读写全部到主机的一种架构
在这里插入图片描述
虽然架构上是类似的,但由于集群里面的服务器数量更多,导致了复杂度整体上更高一些, 具体体现在:

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

目前开源的数据集中式集群以Zookeeper为典型, ZooKeeper 通过 ZAB 协议来解决上述提 到的几个问题,但 ZAB 协议比较复杂(类似Paxos算法),如果我们需要自己去实现ZAB协议,那么复杂度同样会非常高

数据分散集群

数据分散集群指多个服务器组成一个集群,每台服务器都会负责存储一部分数据,同时,为了提升硬件利用率,每台服务器又会备份一部分数据。
数据分散集群的复杂点在于如何将数据分配到不同的服务器上,算法需要考虑如下设计点:

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

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

Hadoop 的实现就是独立的服务器负责数据分区的分配,这台服务器叫作 Namenode o Hadoop 的数据分区管理架构如下图所示。
在这里插入图片描述
HDFS采用master/slave架构,一个HDFS集群由一个Namenode和一定数目的Data nodes组成,Name node是一个中心服务器,负责管理文件系统的名字空间(namespace),以及客户端对文件的访问

集群中的Data node 一般是一个节点一个,负责管理它所在节点上的存储。 HDFS暴露了文件系统的名字空间,用户能够以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组Data node上

Namenode执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。 它也负责确定数据块到具体Data node节点的映射,Data node 负责处理文件系统客户端的读写请求,在Namenode的统一调度下进行数据块的创建、删除和复制操作

与Hadoop不同的是, Elasticsearch集群通过选举一台服务器来做数据分区的分配,叫作master node ,其数据分区管理架构如下图所示
在这里插入图片描述
主节点负责集群范围内的轻量级操作,例如创建或删除索引,跟踪哪些节点是集群的一部分以及确定将哪些分片分配给哪些节点。 拥有稳定的主节点对于群集健康非常重要

数据集中集群架构中,客户端只能将数据写到主机;数据分散集群架构中,客户端可以向 任意服务器中读写数据。正是因为这个关键的差异,决定了两种集群的应用场景不 同 。一般来说,数据集中式集群适合数据量不大,集群机器数量不多的场景 。例如,ZooKeeper 集群,一 般推荐5台机器左右,数据量是单台服务器就能够支撑;而数据分散式集群,由于其良好的可 伸缩性,适合业务数据量巨大,集群机器数量庞大的业务场景。例如,Hadoop集群 、HBase 集 群,大规模的集群可以达到上百台甚至上千台服务器

分布式事务算法

某些业务场景需要事务来保证数据一致性,如果采用了数据集群的方案,那么这些数据可能分布在不同的集群节点上,由于节点间只能通过消息进行通信,因此分布式事务实现起来只 能依赖消息通知 。但消息本身并不是可靠的,消息可能丢失 , 这就给分布式事务 的实现带来了复杂性

分布式事务算法中比较有名的是“二阶段提交”(Two-phase commit protocol ,简称 2PC)和 “三阶段提交”(Three-phase commit protocol ,简称 3PC)

2PC

二阶段提交算法主要由两个阶段组成,分别是Commit请求阶段和Commit 提交阶段 。二阶段提交算法的成立基于以下假设:

  • 在分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts ),且节点之间可以进行网络通信
  • 所有节点都采用预写式日志,且日志被写入后即保持在可靠的存储设备上,即使节点损坏,也不会导致日志数据的消失
  • 所有节点不会永久性损坏,即使损坏,仍然可以恢复
算法基本说明
第 一阶段(提交请求阶段)
  • 协调者向所有参与者发送 QUERY TO COMMIT 消息,询问是否可以执行提交事务, 并开始等待各参与者的响应
  • 参与者执行询问发起为止的所有事务操作,并将 Undo 信息和 Redo 信息写入日志, 返回 Yes 消息给协调者;如果参与者执行失败,则返回 No 消息给协调者,示意图如下
    在这里插入图片描述
    有时候,第一阶段也被称作投票阶段,即各参与者投票是否要继续接下来的提交操作
第二阶段(提交执行阶段)

【成功】
当协调者从所有参与者获得的相应消息都为“Yes ”时

  • 协调者向所有参与者发出"COMMIT"的请求
  • 参与者完成 COMMIT 操作,并释放在整个事务期间占用的资源
  • 参与者向协调者发送"ACK"消息
  • 协调者收到所有参与者反馈的"ACK"消息后,完成事务
    【失败】
    当任一参与者在第一阶段返回的响应消息为“No 气或者协调者在第一阶段的询问超时之 前无法获取所有参与者的响应消息时 :
  • 协调者向所有参与者发出“ ROLLBACK ”的请求
  • 参与者利用之前写入的Undo信息执行回滚,并释放在整个事务期间占用的资源
  • 参与者向协调者发送“ACK ”消息
  • 协调者收到所有参与者反馈的“ ACK ”消息后,取消事务
    示意图如下
    在这里插入图片描述
    有时候,第二阶段也被称作完成阶段,因为无论结果怎样,协调者都必须在此阶段结束当前事务

2PC是强一致性算法,优点是实现简单,但缺点也很明显

  • 同步阻塞 :在整个算法的执行过程中,协调者与参与者互相等待对方的响应消息,等待过程中节点处于阻塞状态,不能做其他事情,如果某个节点响应消息比较慢,则整个系统全部被拖慢,导致 2PC 的性能存在明显问题,难以支撑高并发的应用场景
  • 状态不一致:在第二阶段的执行过程中,如果协调者在发出 commit 请求消息后,某个 参与者并没有收到这条消息,其他参与者收到了这条消息,那么收到消息的参与者会提 交事务,未收到消息的参与者超时后会回滚事务,导致事务状态不一致。虽然协调者在 这种情况下可以再发送 ROLLBACK 消息给各参与者 ,但这条 ROLLBACK 消息一样存 在丢失问题,所以极端情况下无论怎么处理都可能出现状态不一致的情况
  • 单点故障 : 协调者是整个算法的单点,如果协调者故障,则参与者会一直阻塞下去。比 如在第二阶段中,如果协调者因为故障不能正常发送事务提交或回滚通知,那么参与者 们将一直处于阻塞状态,整个数据库集群将无法提供服务。

3PC

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

算法基本说明
第一阶段(提交判断阶段)
  • 协调者向参与者发送 canCommit 消息,询问参与者是否可以提交事务
  • 参与者收到 can Commit 消息后,判别 自 己是否可以提交该事务, 如果可以执行就返回 yes ,不可以则返回 no
  • 如果协调者收到任何一个 no 或参与者超时,则 事务终止 ,同时会通知参与者终止事务 ,如果在超时时间内收到所有yes,则进入第二阶段。
第二阶段( 准备提交阶段)
  • 协调者发送 preCommit 消息给所有参与者,告诉参与者准备提交
  • 参与者收到 preCommit 消 息后,执行事务操作 , 将undo和redo信息记录到事务日志中, 然后返回 ACK消息
第三阶段(提交执行阶段)
  • 协调者在接收到所有ACK消息后会发送 doCornmit,告诉参与者正式提交;否则会给参与者发出终止消息 , 事务回滚
  • 参与者收到do Commit消息后提交事务,然后返回haveCommitted消息
  • 如果参与者收到一个 preCornrnit 消息并返回了 ACK,但等待 doCornrnit 消息超时(例如协调者崩溃或超时),参与者则会在超时后继续提交事务,算法流程图如下:
    在这里插入图片描述
    三阶段提交算法虽然避免了二阶段提交算法的协调者单点故障导致系统阻塞的问题,但同样存在数据不一致问题

分布式一致性算法

分布式事务算法的主要目的是为了保证分散在多个节点上的数据统一提交或回滚,以满足ACID 的要求,而分布式一致性算法的主要目的是为了保证同一份数据在多个节点上的一致性, 以满足CAP中的CP要求
复制状态机是实现分布式一致性的常用技术,其主要角色有三个

  • 副本: 多个分布式服务器组成一个集群,每个服务器都包含完整状态机的一个副本
  • 状态机: 状态机接受输入,然后执行操作,将状态改变为下一个状态
  • 算法 : 使用算法来协调各个副本的处理逻辑,使得副本的状态机保持一致

复制状态机的核心就是分布式一致性算法

  • Paxos
  • Raft
  • ZAB
    ZAB的全称是 ZooKeeper Atomic Broadcast Protocol ,是Zoo Keeper系统中采用的分布式一致性算法。抛开各种实现细节, ZAB的实现和Raft其实是类似的,例如,强化Leader 的作用, 通过Leader 来保证分布式一致性,ZAB肯定也是 Paxos 的一个不完整版
    ZAB、Paxos和Raft有一个较大的差异就是复制的方式, Paxos 和 Raft 采用的是 state machine replication(又称 active replication), ZAB采用的是 primary backup(又称 passive replication), 两种实现方式差异如下:
    • state machine replication :各个节点间 复制的是具体的操作,然后每个节点自己执行操作
    • primary backup: Leader 节点执行操作,将执行结果复制给其他节点

假如我们实现一个 K- V 存储系统,当前系统中 X=3 ,收到 Client 发送的“ SET X=X+1 ”请求,如果是 state machine replication ,则每个节点都会收到“ SET X=X+1”这条操作,达成共识后各自执行;如果是 primary backup ,则会由 Leader 节点先执行“ X=X+1 ”操作得到 X=4 ,达成共识后,其他节点将 X 的值设为 4

数据分区

数据分区指将数据按照一定的规则进行分区,不同分区分布在不同的地理位置上,每个分 区存储一部分数据,通过这种方式来规避地理级别的故障所造成的巨大影响 。 采用了数据分区 的架构后,即使某个地区发生严重的自然灾害或事故,受影响的也只是一部分数据,而不是全 部数据都不可用;当故障恢复后,其他地区备份的数据也可以帮助故障地区快速恢复业务
设计一个良好的数据分区架构,需要从多方面去考虑。

数据量

数据量的大小直接决定了分区的规则复杂度,数据量大就需要更多的存储,存储多了出现故障就难排查,更新存储配置起来也就更复杂,大到足够程度还要考虑分区和复制

分区规则

  • 地理位置有近有远,因此可以得到不同的分区规则,包括洲际分区、国家分区、城市分区 。 具体采取哪种或哪几种规则,需要综合考虑业务范围、成本等因素
  • 通常情况下,洲际分区主要用于面向不同大洲提供服务,由于跨洲通信的网络延迟己经大到不适合提供在线服务了,因此洲际间的数据中心可以不互通或仅作为备份;国家分区主要用于面向不同国家的用户提供服务,不同国家有不同的语言、法律、业务等,国家间的分区一般 也仅作为备份;城市分区由于都在同一个国家或地区内,网络延迟较低,业务相似,分区同时 对外提供服务,可以满足业务多活之类的需求

复制规则

常见的分区复制规则有三种 : 集中式、互备式和独立式

集中式

集中式备份指存在一个总的备份中心,所有的分区都将数据备份到备份中心 ,其基本架构 如下图所示
在这里插入图片描述
集中式备份架构具备如下优缺点:

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

互备式

互备式备份指每个分区各份另外一个分区的数据,其基本架构如下图所示
在这里插入图片描述
互备式备份架构具有如下优缺点 :

  • 设计比较复杂,各个分区除了要承担业务数据存储 ,还需要承担备份功能 ,相互之间互相关联和影响
  • 扩展麻烦,如果增加一个武汉分区,则需要修改广州分区的复制指向武汉分区,然后将武汉分区的复制指向北京分区。而原有北京分区已经备份了的广州分区的数据怎么处理也是个难题 ,不管是做数据迁移,还是广州分区历史数据保留在北京分区,新数据备份到武汉分区, 无论哪种方式都很麻烦
  • 成本低,直接利用己有的设备

独立式

独立式备份指每个分区自己有独立的备份中心,其基本架构如下图所示
在这里插入图片描述
有一个细节需要特别注意 ,各个分区的备份并不和原来的分区在一个地方 。 例如,北京分区的备份放到了天津,上海的备份放到了杭州, 广州的备份放到了汕头,这样做的主要目的是规避同城或相同地理位置同时发生灾难性故障的极端情况,如果北京分区机房在朝阳区,而备 份机房放在通州区,整个北京停电的话,两个机房都无法工作 。
独立式备份架构具有如下优缺点:

  • 设计简单,各分区互不影响
  • 扩展容易,新增加的分区只 需要搭建自己的备份中心即可
  • 成本高,每个分区需要独立的备份中 心,这个成本 比集中式备份都要高很多,因为备 份中 心的场地成本是主要成本

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/108594971