从时序图出发详解PC算法

在分布式系统中,为了保证数据的高可用,通常,我们会将数据保留多个副本(replica),这些副本会放置在不同的物理的机器上。为了对用户提供正确的增\删\改\差等语义,我们需要保证这些放置在不同物理机器上的副本是一致的。

而PC则是为了解决不同物理机器上的副本的事务数据一致性的问题。

在分布式系统中事务与单机应用事务不一样的地方是:可能会因为服务宕机或者网络不通的情况而导致数据不一致,接下来让我们按照这个思路来逐个分析1PC、2PC、3PC的优缺点:
(时序图没有画得很详细,需要详细的图可以搜索其他博客)

1PC

在这里插入图片描述

从网络方面,在以下阶段出现网络异常:

①:客户端无法发起事务,不会导致数据不一致。

②:向B系统发起事务请求失败,A回滚,不会导致数据不一致。

③:B已经commit,而A请求超时,多次重试后rollback,出现数据不一致。

④:返回客户端响应,AB已经达到数据一致性,不会导致数据不一致。

从服务宕机方面:

若在A向B发出commit请求后A宕机,则会导致B commit而Arollback。

1PC的有点是best effect。在所有分布式事务算法中该运行速度是最快的,但是在遇到异常情况时候也容易造成数据的不一致,需要人工介入维护。

2PC

在这里插入图片描述

首先在参与者第二阶段开始事务到全部完成事务这段时期,是存在数据不一致的情况的,但由于各个参与者是提前执行完事务,最后再进行确定,这段时期很短。下面分析的是会出现长时间的数据不一致状态的情况。

从网络方面,在以下阶段出现网络异常:

①-⑤:由于只是准备阶段,若此期间出现网络异常则取消事务,不会导致数据不一致。

⑥⑧:为了减少同步造成的资源阻塞,许多事务管理器都采用了多线程的方式处理,因此当该阶段中存在部分参与者网络异常。这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是便出现了数据不一致性的现象。

⑦⑨:参与已经commit,而事务管理器请求超时,多次重试后执行rollback,于是出现数据不一致。

⑩:返回客户端响应,AB已经达到数据一致性,不会导致数据不一致。

从服务宕机方面:

情况一:协调者宕机了,参与者没宕机

只要找一个协调者的替代者。当他成为新的协调者的时候,询问所有参与者的最后那条事务的执行情况,协调者就可以知道是应该做什么样的操作了。所以,这种情况不会导致数据不一致

情况二:参与者宕机了,协调者没宕机

如果参与者宕机了。那么之后的事情有两种情况:

  • 第一种是宕机了就算了,没有再恢复。那宕机的机器此时是不可用的状态,里面的数据不会被其他应用访问,因此不会造成数据不一致
  • 第二种是宕机了之后又恢复了,参与者会询问协调者如何保持数据一致性,协调者就会比对自己的事务执行记录和该参与者的事务执行记录,告诉该参与应该怎么做来保持数据的一致性

存在问题:第一阶段如果宕机的参与者返回的是否,而其他参与者返回的是是,那么此时协调者按照剩下的参与者第二阶段使用了commit操作。而后面之前宕机的参与者此时恢复了,为了保持数据一致性需要强行commit么?

情况三:参与者宕机了,协调者也宕机了

  • 协调者和参与者在第一阶段宕机了:

    由于这时还没有执行commit操作,新出来的协调者依次询问各个参与者的情况,再决定是进行commit还是roolback。因为还没有commit,所以不会导致数据一致性问题。

  • 第二阶段协调者和参与者宕机,宕机的这个参与者在挂之前并没有接收到协调者的指令,或者接收到指令之后还没来的及做commit或者roolback操作:

    这种情况下,当新的协调者被选出来之后,他同样是询问所有的参与者的情况。

    • 只要有机器执行了abort(roolback)操作或者第一阶段有参与返回的信息是No的话,那就直接执行roolback操作。
    • 如果有机器执行了commit操作,那么就直接执行commit操作。

    这样,当挂掉的参与者恢复之后,只要按照协调者的指示进行事务的commit还是roolback操作就可以了。因为挂掉的机器并没有做commit或者roolback操作,而没有挂掉的机器们和新的协调者又执行了同样的操作,那么这种情况不会导致数据不一致现象。

  • 第二阶段协调者和参与者宕机,宕机的这个参与者在挂之前已经执行了操作。但是由于他宕机,没有人知道他执行了什么操作。

    大家肯定会想,在协调者的影响下参与者们不都是执行同一种操作么,怎么可能在一个事务中既出现commit又出现roolback的,我们可以看下下面这种情况:

    当除了宕机的参与者第一阶段响应否,而其他参与者第一阶段响应是,在第二阶段时协调者调用了abort(roolback)操作,此时将要宕机的参与者执行了roolback操作。但其他参与者由于网络问题没有收到协调者的命令。当将要宕机的参与者回滚完毕并响应后,协调者跟该参与者都宕机了。后面协调者恢复后发现存活的参与者第一阶段都响应是,那么就会执行commit,这样不就导致了数据不一致了么。

    因此我们可以总结下出现该问题的原因是,在第一阶段某个参与者未宕机的情况下存在否,同时因为网络异常导致除了将要宕机的参与者之外未收到abort指令,但是在该参与者宕机后整体不存在否,由此出现数据不一致。

    这种情况下,新的协调者被选出来之后,依次询问各个参与者的情况,再决定是进行commit还是roolback。这样新的协调者和所有没挂掉的参与者就保持了数据的一致性,我们假定此时执行了commit。但是,当之前宕机的参与者恢复了,因为该参与者之前已经执行完了之前的事务,如果他执行的是commit那还好,和其他的机器保持一致了,万一他执行的是roolback操作就导致数据的不一致性。

所以,2PC协议中,如果出现协调者和参与者都宕机的情况,有可能导致数据不一致。

二阶段提交看起来确实能够提供原子性的操作,但是不幸的是,二阶段提交还是有几个缺点的:

1、同步阻塞问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源时不得不处于阻塞状态。一般使用多线程执行加快公共资源的释放。

2、单点故障:由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,直到新的协调者被选举出来。

3、数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送请求过程中协调者与参与者发生了故障是会出现较长时间的数据不一致情况。其实2PC为了高可用采用了最终一致性,因此在参与者第二阶段开始事务到全部完成事务这段时期分布式系统都会处于数据不一致的情况。

4、存在事务状态不确定的隐患:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

3PC

在这里插入图片描述
三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase commit protocol),是二阶段提交(2PC)的改进版本。

与两阶段提交不同的是,三阶段提交有两个改动点:

  • 引入超时机制。同时在协调者和参与者中都引入超时机制。
  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommitPreCommitDoCommit三个阶段。

从网络方面,在以下阶段出现网络异常:

①-②:在该两个阶段中,并没有事务的预提交,因此不会造成数据不一致。

③:如果只有部分参与者收到了pre-commit请求,在协调者尝试超时后仍然没有所有参与者返回ACK响应,则发送abort请求回滚。若此时⑤由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

④:如果参与者在收到了pre-commit请求后,执行了事务,在协调者尝试超时后仍然没有所有参与者返回ACK响应,则发送abort请求回滚。若此时⑤由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

⑤:若在协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

⑥:返回响应,参与者已经达到数据一致性,不会导致数据不一致。

从服务宕机方面:

情况一:协调者宕机了,参与者没宕机

只要找一个协调者的替代者。当他成为新的协调者的时候,询问所有参与者的最后那条事务的执行情况,协调者就可以知道是应该做什么样的操作了。所以,这种情况不会导致数据不一致

情况二:参与者宕机了,协调者没宕机

如果参与者宕机了。那么之后的事情有两种情况:

  • 第一种是宕机了就算了,没有再恢复。那宕机的机器此时是不可用的状态,里面的数据不会被其他应用访问,因此不会造成数据不一致
  • 第二种是宕机了之后又恢复了,参与者会询问协调者如何保持数据一致性,协调者就会比对自己的事务执行记录和该参与者的事务执行记录,告诉该参与应该怎么做来保持数据的一致性

情况三:参与者宕机了,协调者也宕机了

  • 协调者和参与者在第一、二阶段宕机了:

    由于这时还没有执行commit操作,新出来的协调者依次询问各个参与者的情况,再决定是进行commit还是roolback。因为还没有commit,所以不会导致数据一致性问题。

  • 第三阶段协调者和参与者宕机,宕机的这个参与者在挂之前并没有接收到协调者的指令,或者接收到指令之后还没来的及做commit或者roolback操作:

    这种情况下,当新的协调者被选出来之后,他同样是询问所有的参与者的情况。

    • 只要有机器执行了abort(roolback)操作或者第二阶段有参与返回的信息是No的话,那就直接执行roolback操作。
    • 如果有机器执行了commit操作,那么就直接执行commit操作。

    这样,当挂掉的参与者恢复之后,只要按照协调者的指示进行事务的commit还是roolback操作就可以了。因为挂掉的机器并没有做commit或者roolback操作,而没有挂掉的机器们和新的协调者又执行了同样的操作,那么这种情况不会导致数据不一致现象。

  • 第三阶段协调者和参与者宕机,宕机的这个参与者在挂之前已经执行了操作。但是由于他宕机,没有人知道他执行了什么操作。

    我们假设挂掉的那台参与者执行的操作是doCommit。那么其他没挂的操作者的状态要么是prepare-commit要么是doCommit。因为3PC的第三阶段一旦有机器执行了doCommit,那必然第一阶段大家都是同意doCommit。所以,这时,新选举出来的协调者一旦发现未挂掉的参与者中有人处于doCommit状态,那就执行doCommit操作。否则,代表在协调者宕机前,大概率还处于第二阶段的状态,尚未进入到第三阶段,就执行rollback操作,直接rollback整个事务。这样挂掉的参与者恢复之后就能和其他机器保持数据一致性了。前提是要程序具有幂等性。

根据上面的分析我们可以看到:3PC解决了2PC存在的以下问题

单点故障与同步阻塞问题:

由于同时在协调者和参与者中都引入超时机制,在出现单点故障的情况下,参与者们也可以按照策略来进行事务的提交,从而避免长时间的同步堵塞问题。同时,在协调者第三阶段超时的情况下,参与者们可以主动commit,也避免了因为网络环境而导致共享资源被锁定。

存在事务状态不确定的隐患:

通过将2PC的第一阶段划分为两个阶段,增加了判断协调者宕机前所在进度的条件,从而避免了事务状态不确定的情况。但必须要保证程序具有幂等性。

由此,无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。

总结

根据CAP、BASE原理,在分布式条件下为了保证可用性,我们往往采用最终一致性。于是在以上的算法中,都无法解决分布式的一致性问题,那么我们需要面对的就是尽量防止出现事务状态不确定的情况,而对于临时的数据不一致,一般采用超时重传的方式,一般网络条件在少数几次重传后便会恢复正常。但是在一些物理性的破坏下会出现长时间的网络分区,这时候我们需要让参与者在暂时没有协调的情况下自行处理事务,采用rollback或commit,以容忍数据不一致来换取共享资源的释放。

1PC、2PC、3PC算法的采用一般取决于生产环境对数据不一致的容忍程度,越是复杂的算法就越是消耗性能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值