分布式系统杂谈

一、单机的痛点

当我们使用单台机器对外提供服务时,有如下两个主要问题:

第一、单台机器的性能已经无法满足我们的要求

第二、单台机器容易造成单点故障,导致服务可用性不高

二、分布式系统

分布式就是为了解决这两个问题应运而生的。具体来讲,我们通常可以通过如下方式来解决这两个问题

1.数据分区:将每个数据块分别放在不同的机器上,这样可以解决单台机器的性能问题,却无法解决单点故障问题

2.数据镜像:让所有的服务器都保存该数据。它可以用来解决单点故障问题

但是这两种方式却带来新的问题,下面我们以最经典的案例来说明:A向B转账200元

这个案例需要分为如下六个步骤:

1.读取A的账户余额

2.将A的账户余额减去200

3.设置A的账户余额

4.读取B的账户余额

5.将B的账户余额增加200

6设置B的账户余额

当然这六个步骤是需要事务化的执行,即要么全做,要么一个也不做。否则的话,容易导致A减了200,B却没有增加等错误情况发生。

当我们采用数据分区时:

假设A和B的账户资料不在同一个分区上,这样便产生了跨机器的分布式事务一致性问题

当我们采用数据镜像时:

当我们对一个数据镜像上的A和B的账户余额进行操作时,如何将数据的更改同步到其他镜像?

当有两个镜像分别对A的账户余额进行操作时,我们如何写操作的一致性?

同样是跨机器分布式一致性问题。

三、分布式的痛点

首先我们来介绍一下CAP定理

C:Consistency,即(强)一致性。表示任何时间在不同的节点访问到的数据都是相同的。

A:Availablity,可用性。即不论什么情况下,集群都可以正常对外提供服务。

P:Partition tolerance,分区容忍性,即是否可以容忍将集群划分成不可连接的分区。(在互联网中,由于网络的不稳定性、以及宕机的风险存在,分区容忍性是必须要满足的)

CAP定理表示:我们不可能同时满足CAP三个条件,最多只能同时满足其中两个。举例来说:

1)当我们需要满足A和P时,为了满足A,需要在多个分区有数据备份,那么由于网络的隔离,则无法保证C。

2)当我们需要满足C和A时,同样为了A,需要有多个备份,并且备份间网络互通才能保证C,则P无法满足。

3)当我们需要满足P和C时,由于网络隔离,并且需要强一致性,则不能有备份,这样的话容易产生单点故障,则无法保证A。

当然,由于CAP定理中所说的C是指强一致性,那么在互联网P条件是一定的情况下,我们可以通过降低一致性的方法,来提高可用性(当然这种情况下的可用性,也是低于只满足P、而不满足C的情况的,无非是权衡罢了)。所以CAP不是必须3选2的,而是三者都可以得到一定程度上的满足,注意是一定程度上的,而不是完全满足。

四、分布式一致性

分布式一致性分为两种:弱一致性和强一致性,其中弱一致性又包括最终一致性。

弱一致性:当我们修改一个数据后,无法保证在任何节点上获取的数据完全一致。

最终一致性:当我们修改其中一个数据时,虽然最开始通过不同节点获取的数据可能不同,但是经过一段时间后其一定相同。

强一致性:我们修改一个数据后,任何时候,通过任何节点获取的数据一定相同。

下面我们先通过主从架构为例分别进行说明,架构图如下(请忽略我的渣画工):

1)向master中写数据时,只有向所有的slave同步成功时,才返回成功,否则只要有一个slave写入失败,则回滚。这就是强一致性的实现,由此可见,强一致性一般是由同步的方式实现的。

在网络隔离的情况下,由于不能像所有slave中成功写入数据,所以写入可用性得不到保障。这种方式适合银行等对数据一致性要求非常高的应用场景(例如存款数据)下使用。

2)向master中写数据时,只要向其中的一个或者几个(非全部)slave写入成功时就返回成功,其他未写入的slave则等待后续的异步写入。这就是最终一致性的实现,由上可见,弱一致性一般是由异步的方式实现的。这样即使在存在网络隔离的情况下,假如需要写入的slave和master在一个网络分区中,写入可用性也是存在的。所以这种情况相当于牺牲了部分一致性来换取了部分可用性,也印证了上面所说的“CAP中三者是可以在一定程度上存在的”这种论点。这种方式在互联网这种对数据一致性要求不高的场景下(例如评论数据等)应用比较多。

另外,在这种处理模式下,master写入成功,slave写入失败时则有两种处理方法:

第一:回滚所有的成功写入,并返回失败

第二:标记写入失败的slave为不可用,返回成功,当该slave恢复可用时,则从master同步该数据。

五、分布式事务

事务分为局部事务和全局事务,局部事务是指本地事务,而全局事务则指跨机器的事务,也就是分布式事务。分布式事务的研究经久不衰,目前业内比较典型的分布式事务有两阶段提交、三阶段提交等等。下面我来讲讲对于这几个算法的理解。

5.1 两阶段提交

由于一个节点很难知道其他节点事务的执行情况,所以在实现分布式事务时,通常需要一个协调者的角色,两阶段提交也不例外。

所谓两阶段提交,就是指将分布式事务的提交分成两个部分:预备部分和提交部分。有人说预备部分像西方婚礼中的宣誓部分(征求男女双方是否愿意),提交部分则像是婚礼执行部分,我觉得不是很准确,理由下面我会讲到。

请求阶段:

1.协调者向参与者发送prepare request

2.参与者收到消息后,锁定事务所需要的资源并开始执行事务但并不提交,并将事务执行结果发送给协调者。由于这里对事务所需要的资源进行了锁定,并且执行完了事务(只是没有提交),所以说上面所说的拿西方婚礼宣誓部分来表示该阶段是不太准确的(宣誓只是征求意愿,并没有执行婚礼)。

提交阶段:

1.协调者收到参与者返回的消息后,有三种可能:

    (1) 全部收到YES,则再发送commit消息给参与者,提示参与者提交事务

    (2) 收到有至少一个NO,则发送abort给参与者,表示回滚事务

    (3) 超时未收到(某参与者的)回复,重试或者标记该参与者为不可用

2.参与者收到1中协调者发来的消息,有两种可能:

    (1) 如果收到commit消息,则提交事务

    (2) 如果收到abort消息,则回滚事务

同时,返回确认给协调者。

上述就是两阶段提交的执行过程。下面我们来分析下两阶段提交的缺点:

1.由于两阶段提交的分布式事务是同步的,所以性能是其一大短板。

2.协调者的单点问题。

3.当协调者给参与者发送commit后,由于网络抖动或者协调者宕机,导致部分节点收到commit消息、而部分节点没有收到,则会导致不一致的情况产生。

4.TIMEOUT情况的发生所导致的问题,其中一个比较头疼的情况是:由于网络抖动等原因而导致的接收不到请求包或者响应包的情况。当在分布式事务中的时候,由于参与者没有延时机制,假如参与者无法接收到协调者发来的消息,那么参与者将处于不知所措的状态,其将永远处于该事务的执行中而无法退出。接下来所说的三阶段提交就是为了解决这个问题而生。

 5.2 三阶段提交

为了解决上述第4个问题,三阶段提交引进了参与者延时机制以及CanCommit阶段。概括来说,引入CanCommit阶段来增强判断事务执行成功的判断正确的可能性,以便于在参与者延时的时候猜测事务执行成功而提交事务,而不用像二阶段提交中参与者那样的不知所措。

下面讲一下三阶段提交的步骤:

CanCommit:

1.协调者向参与者发送请求,征求其是否可以执行事务提交。

2.参与者接收到该请求后,并不锁定资源,也不执行事务,而是根据其他的一些条件判断该事务是否可执行并提交。如果可以,则返回YES,否则返回NO

PreCommit与DoCommit:

这两个阶段与两阶段提交的两步基本相同,只不过是增加了参与者延时机制。此时如果参与者由于网络抖动或者协调者宕机,导致参与者无法接收到协调者发来的消息,由于经过CanCommit阶段的预判,那么事务提交成功的成功率则大大提高,所以此时参与者则会提交事务。

虽然三阶段提交解决了二阶段提交的第4个问题,然而一致性问题却没有得到解决。例如,在PreCommit和DoCommit阶段中,由于某些原因导致协调者发送回滚事务的消息时,一些参与者无法接收到该消息。那么这些参与者则会提交事务,而另外收到消息的参与者则回滚事务,此时便产生了不一致性。

六 分布式一致性算法

paxos:

raft:

未完待续...

猜你喜欢

转载自www.cnblogs.com/levy5307/p/9229655.html