分布式中间件系统的一致性和可用性

对于单机系统和集中系统是指一台或多台主计算机组成的中心节点,并数据和业务处理逻辑都集中于这个中心节点上,客户端仅仅负责数据的录入和展示。集中式系统的最大优点就是部署简单,同时不需要考虑分布式系统的协作问题。

1.单机系统的可用性和一致性(ACID)

对于单机系统最主要的指标就是数据的一致性和可用性。其可以用ACID来度量一个系统的可用性和一致性。
Atomicity原子性:一个事务中所有操作都必须全部完成,要么全部不完成。(redo和undo日志)
Consistency一致性: 不管同时并发的执行多少个事务,在事务开始或结束时,数据库应该在一致状态。(写操作属于当前读会加互斥锁,本质上是顺序执行)。系统只能从一个一致性状态转移到另一个一致性状态,不会出现中间状态(事务只有部分成功)
Isolation隔离性: 事务将假定只有它自己在操作数据,彼此不知晓。(读操作属于快照读,只读快照数据,各个事务执行时间不同,快照不同,不同事务之间不相互影响)。数据库具有四种不同的隔离级别,可以参看mysql的可重复读的隔离级别。
Durability持久性:一旦事务提交完成,就不能回滚,更新也不会丢失。
其中单机数据库就具有ACID的特性。

2.分布式系统的可用性和一致性(CAP)

分布式系统是指被部署在不同网络环境、不同节点的系统。其通过数据的冗余来达到高可用(主备机切换)、高吞吐(备机分担读压力)的能力。由于分布式系统中引入网络,而网络问题是不可避免的,因此如果还要求数据在各个分布式系统之间的强一致性,就会严重的影响服务的可用性(如:数据库的主从拷贝,需要等到所有的变更拷贝到从库,才能提供服务,特别是其中一个从库不可达的时候,那么服务就变得不可用)。然而可用性在大部分系统中又是基本性能指标,因此在设计分布式系统时会根据其业务特性在一致性和可用性之间进行权衡。其比较著名的理论就是CAP理论,其指分布式系统在一致性Consistency、可用性Availability、分区容错性Partition tolerance只能同时满足其中两种。

2.1 CAP理论

首先我们来看下分布式系统中不可避免的网络问题。
1)通信异常问题
在单机系统中,对于客户端的请求或者一个事务的执行,只会出现成功或者失败两种确定的状态,然而在分布式的系统中还可能出现超时这样一种不确定的状态。超时一般存在两种状态:1)由于网络异常,客户端的请求没有达到服务器端 2)客户请求到达服务器端,并且服务器执行成功,但是在返回响应结果的时候,由于网络异常响应结果丢失,造成客户端请求超时。因此对于网络异常是一种不确定的状态,因此其影响系统的可用性。
2)网络分区
当遇到严重的网络异常时,导致分布式系统中部分节点之间不可达,而其他部分节点内部是可以通信的,使得整个分布式系统分割成了一个一个小的集群,这些小的集群可以正常的处理集群内部的事务,但是跨集群之间的事务总是失败的(拿银行系统来举例,就是单个银行内部的存款取款功能正常,但是跨行的转账业务不可用),这样的想象就是所谓的网络分区或者脑裂想象。
对于网络问题,是分布式系统不可避免的想象,并且是总会发生的现象,因此要求分布式系统必须具有分区容错性,不能因为网络分区现象的存在使得整个服务不可用。

CAP定理

  • C(一致性):所有的节点上的数据时刻保持同步,对于事务只能同时的成功或者失败,即所有的备份数据已经完全和主数据完全一致。
    强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大。对于每个事务在提交后,只能出现成功或者失败两种状态,不存在中间状态。
    弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不久承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。
    最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型。
    顺序一致性:是指每个提交的事务会根据提交的顺序,使得系统的数据达到最终一致性状态。但是我们知道分布式系统是没有全局时钟的概念,因此在度量分布式事务的顺序时是不准确的,因此顺序一致性大部分是指同一节点提交事务的顺序,不是不同节点之间的顺序性。其中顺序最终一致性常用于redis等分布式数据库系统中。

  • A(可用性):每个请求都能在指定时间内接受到一个准确的响应(成功或者失败)。然而在分布式系统中还会出现超时这样一种不确定的状态。
    在分布式系统中通信异常时最常见的一种异常,其是不可避免的,因此在一个请求发生时,如果发生通信异常,就会产生如下两种情况:1)请求没有到达目标系统,造成整个请求超时(请求未处理)。2)请求到达目标系统,但是在目标返回响应的过程中,响应信息丢失,造成整个请求超时(请求已经处理)。因此超时对于客户系统而言都是一种不确定的状态,其服务是不可用的状态。对于一个分布式系统假设每个机器或者节点产生网络故障的概率是P,那么整个分布式系统的极致可用性是1-P^N(N表示机器或者节点数量)。因此此处所说的可用性是指分布式系统的极致可用性,即只要有一台机器或者节点可用那么就要保证整个服务可用。

  • P(分区容错性):系统应该能持续提供服务,即使由于网络原因造成系统被分隔成很多个子系统(分区),任然能够提供服务。网络问题是分布式系统不可避免的问题,并且是一定会发生的问题。因此对于分布式系统必须要具有分区容错性。例如:一个分布式系统需要有3个备份,其分区容错性为2,及要求必须有2个备份才能继续提供服务。因此如果其中一个备份的机器节点不可达,由于分区容错性的存在,其可以继续提供服务,并且分布式系统会根据情况等待该分区重新连接,恢复失去连接时的新增数据,之后再提供服务。或者由于节点宕机,会重新产生一个分区,并同步所有的数据(时间较长),等数据同步完成再开始进行服务。如果一个分布式系统不允许分区容错性,那么只要一个备份数据不可用,整个服务都不可用,这是用户不可接受的。

综合来看大部分的分布式系统都是在考虑满足P的情况下,在C和A之间进行选择,并且可能会根据一个系统中业务的不同分别的满足CP或者AP的特性,甚至在C和A之间寻找一个平衡点。

PS:同时满足CAP的系统是不存在的,因为如果一个分布式系统要在满足分区容错性的情况下,同时满足CA,那么当一个请求同步到一个分区的时候,请求消息被丢失,那么由于一致性的限定,要求这个分区一定要有这个消息的请求,那么就造成了整个服务不可用,即A不满足。在分布式数据库中,对于写操作就会进行回滚,最终就导致了写操作的不可用。

3 分布式系统在CAP中的选型

3.1 CA类型系统

单机数据库(CA类型):由于数据库的是以事务为单位进行执行的而事务具有ACID的特性,因此能够保证数据时刻都是同步的。并且只要整个网络不瘫痪(及单机数据库不宕机)都能保证服务是可用的。因此单机数据库系统是具有CA特性的。
在数据库中主要是通过记录log日志(redo日志和undo日志)来保证数据的一致性。通过乐观锁(快照读,根据事务的不同执行时间,提供不同版本的快照数据,达到读不加锁,提高服务的可用性),对于需要加锁悲观锁的操作(update delete alter select for update)通过当前读及对要处理的记录加锁(gap锁)来使得整个操作串行化的目的。

3.2 CP类型系统(分布式数据库)

分布式数据库(半同步复制):主从同步延迟引起的数据不一致,可以通过消除主从延迟或者对有延迟的数据读主库来达到数据的强一致性。
半同步复制:在客户端请求时,等从库同步完成后,再返回。
分布式中间件系统的一致性和可用性_第1张图片
方案优点:利用数据库原生功能,比较简单
方案缺点:主库的写请求时延会增长,吞吐量会降低。极端情况下,网络分区的出现,造成整个服务不可用。

分布式数据库(数据库中间件):由中间件根据主从同步的完成情况来决定是查主库还是从库。
分布式中间件系统的一致性和可用性_第2张图片
方案优点:在中间件性能不存在问题的情况下,吞吐量比半同步复制高很多倍,略低于主从类型数据的写性能。
方案缺点:对中间件性能要求很高,所有的请求都需要经过中间件进行路由,容易造成单点故障,当出现分区时所有请求都路由到主库,容易造成主库宕机。并且读写性能都有略微下降。

分布式数据库(缓存更新记录):预估主从延迟的最大时间作为缓存过期时间,对于刚更新的数据,将key缓存到缓存中,当读请求时,先查缓存,命中查主库,未命中查从库。
方案优点:写性能同直接写主库相比性能下降很少。有较高的吞吐量。
方案缺点:读写都要先操作缓存数据,会对性能有所影响。过期时间难以保证,可能出现不是强一致性的情况。
上述类型的数据库方案都是牺牲了可用性来保证数据的强一致性。

分段提交协议(zookeeper)
略(待补充)。zookeeper通过实现分段提交协议(zab协议)来使得其具有强一致性和分区容错性的特点,其大量用于解决分布式中间件在遇到脑裂时的主-备选举和数据恢复的功能。

在分布式系统中,每个单机系统都可以通过本地事务来准确的知道自己处理的结果是成功或者失败。因此对于分布式系统我们可以提供一个协调者来统一调度分布式节点(参与者)的事务,并根据分布式节点(参与者)的处理结果来决定各个分布式节点的事务是否真正的提交,根据这种思路提出了两阶段提交和三阶段提交的分布式事务协议。

二阶段提交协议:
阶段1:提交事务请求

  • 事务询问。协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并等待各个参与者的回应。
  • 执行事务。各个参与者节点执行事务操作,并将Undo和Redo信息记录事务操作中。
  • 各参与者向协调者反馈事务询问的结果。各个参与者根据事务操作的执行结果反馈,事务是否可以执行。

阶段2:执行事务提交。协调者根据各个参与者的事务操作的执行结果,决定是否需要各个参与者提交事务。事务一旦提交就不可逆。

  • 执行事务提交。如果所有参与者的事务操作都执行成功。那么协调者向参与者节点发送commit请求,各个参与者接收到commit请求,执行事务的提交。参与者在完成事务提交后,发送ACK消息给协调者。协调者接收到ACK后完成分布式事务。
  • 中断事务。如果任何参与者事务操作执行失败以及等待超时还没接收到参与者事务操作执行结果,就会中断事务。协调者会向所有的参与者节点发送事务回滚请求。参与者接受到事务回滚请求后,会根据记录的undo日志记录,进行操作的回滚。参与者回滚完成事务后,向协调者发送ack消息。协调者接收到所有ack消息后,中断分布式事务。

    二阶段协议可以简单的表示为投票阶段和执行阶段。投票阶段试执行并反馈结果。执行阶段根据投票阶段的执行结果,发起事务的提交或者中断操作。

优点:原理简单,实现方便。
缺点:同步阻塞(试运行)、单点问题(协调者)、脑裂(分区,所有操作都失败)、数据不一致(如果协调者只发出部分commit消息就宕机或者发送到部分参与者的commit消息丢失,就会造成数据的不一致性)、容错性差(一旦无法获取到参与者响应,根据超时机制只能进行中断操作)。

三阶段提交协议:
阶段1:CanCommit

  • 事务询问:协调者发送CanCommit请求,询问是否可以执行事务提交操作,等待参与者回应。
  • 参与者反馈回应:参与者根据自身情况,反馈是否能够进行执行事务操作。

阶段2:PreCommit。协调者根据参与者是否能够进行事务操作的反馈结果,决定是进行事务的预提交操作还是中断操作。

  • 预提交操作。如果所有参与者都认为自己可以进行事务操作,则进行预提交操作。协调者向所有参与者发送preCommit请求。参与者接收到preCommit请求,执行事务操作并记录redo和undo信息到事务日志中。参与者向协调者反馈自己事务操作的结果,等待协调者最终事务提交的命令。

  • 中断操作。任何一个参与者在CanCommit阶段反馈自己不能进行事务操作或者协调者等待超时,那么就会进入中断操作。协调者向参与者发送中断请求。无论参与者接收到中断请求或者等待协调者请求超时,那么参与者会中断事务。

阶段3:doCommit

  • 执行提交。所有参与者事务都可以执行成功,协调者将预提交变为提交状态,并向所有参与者发送doCommit消息。参与者接收到doCommit请求后,提交本地事务,并反馈ACK消息。协调者接收到参与者的ack消息,完成事务。
  • 中断事务。任一个参与者返回事务操作失败或者协调者等待超时,协调者向参与者发送中断请求。参与者接收到中断操作,根据undo日志进行回滚。回滚完成后,参与者向协调者发送ack信息。协调者接收到所有ack消息,完成中断事务。

一旦进入阶段3,可能出现如下两种故障。

  • 协调者出现问题。
  • 协调者与参与者的网络出现故障。

只要参与者在doCommit阶段,没有接收到doCommit消息或者abort消息,在超时后,参与者会继续提交事务。
优点:多了一个canCommit阶段,使得阻塞范围更小。对于单点故障也能达到(协调者在阶段3出现故障)数据的最终一致性。
缺点:如果在preCommit阶段,协调者和参与者之间网络出现问题。所有参与者都将提交事务(及时abort异常),这样就造成数据的不一致性。

ZAB(ZooKeeper Atomic Broadcast) 协议:ZAB协议又叫原子广播协议,其类似于二段提交协议,其作为ZooKeeper一致性的核心算法。
分布式中间件系统的一致性和可用性_第3张图片
zookeeper服务器集群中的服务器有三种角色:1)Learder服务所有客户端的写请求,并同步变更到Follower和Observer角色的服务器上。其与过半的Follower保持数据的强一致 2)Follower作为Leader服务器的数据备份,其提供Client对于数据的读请求,同时参与Leader的选举过程和过半写成功策略。 3)Observer角色,其同样作为Leader的数据备份,并处理客户端的数据读取请求。
ZAB协议-消息广播:消息广播与二阶段协议的最大区别就是,二阶段协议有中断操作或者事务回滚
分布式中间件系统的一致性和可用性_第4张图片

  • 客户端的写请求都会被转发Leader服务器上,其将写请求以Propose事务请求发送到所有的Follower角色服务器,并等待Follower的反馈。
  • Follower服务器接收到事务请求,根据事务内容进行执行,并以事务日志的形式(参考数据库的redo和undo事务日志)记录到磁盘,如果事务操作能够正常执行,并且成功记录事务日志后,会向Leader服务器发送ACK消息。如果失败,该Follower则抛弃Leader服务器。
  • Leader接收到过半Follower的ACK消息,则向服务器发送Commit消息,Follower接收到Commit消息后,提交事务,完成整个事务。注意:Leader在发送commit消息前以及提交自己的写请求操作。

    PS:可以发现Leader和过半写成功Follower服务器数据是强一致的,然而一部分延迟的Follower和Observer与Leader数据不是强一致的,但是Zookeeper提供了sync操作(异步操作),所有的读请求都需要等待sync消息完成后,才会进行读取数据并返回结果。

    优点:只需要过半Follower服务器写成功则整个ZooKeeper服务器的写操作完成,不需要等待所有的Follower服务都写成功了才完成写请求。因此提高了写请求的吞吐量。过半写成功保证了在崩溃恢复过程中能够快速的找到一个新的Leader并保持数据的一致性。

ZAB协议-崩溃恢复:当Leader服务器与过半的Follower服务器失去联系或者Leader服务器宕机,则整个ZooKeeper集群进入崩溃恢复过程。
分布式中间件系统的一致性和可用性_第5张图片

保证所有已经提交的事务P2能够在所有服务器提交

场景描述:Leader服务器在发送Commit2消息之后崩溃,ZAB需要保证所有的服务器都能够成功提交P2事务,否则会造成数据的不一致。
分布式中间件系统的一致性和可用性_第6张图片
保证只有Leader服务器提出的P3事务需要被丢弃

场景描述:Leader服务在发送P3事务之后,整个Leader服务器崩溃,导致所有的Follower服务器都没有接收到P3事务,ZAB需要保证Server1在恢复后,需要丢弃P3事务。

针对以上两种情形,ZAB通过将崩溃恢复过程分为三个步骤来保证:1)发现过程,在多个Follower服务器进程中选取一个作为准Leader服务进程 2)同步过程,保证过半的Follower服务器进程同准Leader服务器进程在数据上保持强一致性。3)广播过程,准Leader进程成为Leader进程,可以进行正式的接受客户端事务请求,并进行消息广播流程。

  • 发现流程:因为所有的写请求都会转发到Leader进程,并且对于每一个事务都会分配一个单调递增的ZXID来标识事务。所以在Leader崩溃恢复过程中,只需要选取ZXID最大的事务,就可以省去事务提交和丢弃的工作。
  • 同步流程:在选择出准Leader服务器进程后,需要使得所有Follower同准Leader进程保持数据的一致性。因此准Leader会为每一个Follower服务器准备一个队列,并将那些Follower没有同步的Proposal事务发送到Follower服务器,并紧接着发送一个Commit消息,要求Follower服务器提交事务。当所有的未同步事务都提交Follower本地数据库后,将该Follower加入到Leader的Follower列表中。对于那些没有在Leader服务器上提交的P事务,由于恢复的机器(以前是Leader)的最大ZXID的事务周期小于现阶段Leader(刚崩溃的Leader)的事务ZXID,那么该恢复机器不可能成为新的Leader。在同步准Leader的事务过程中,发现有一个事务在准Leader中不具有,因此要求改服务器回退事务。当有过半的Follower同Leader服务器保持数据的强一致性后,该Leader服务器可以进行消息广播流程。
  • 广播流程:广播流程同上面的消息广播流程的二段提交协议。

3.3 AP类型分布式系统

AP类型是最常见的分布式系统,其只需要在一定时间内能够保证数据的最终一致性。大部分的中间件的实现都保证了AP特性,如redis、主从数据库等。

一主多从-读写分离(atlas)分布式数据库
在一主多从、读写分离的分布式数据库中,主从的同步是通过binlog同步来实现,而binlog的dump过程是单线程的,并且大部分的同步方式都是行级同步,因此当存在大量的写操作时,主库提交的事务,在从库生效会一定的延迟,但是其保证一定会在从库生效。这样的主从数据库就保证了服务的可用性(读从,主从故障切换)的基础上,同时满足了最终一致性的要求,并且其会根据主库中事务提交的顺序(单线程的)在从库生效,其也满足了顺序一致性。

你可能感兴趣的:(分布式系统,一致性算法,CAP)