天天看点

分布式事务2PC和3PC

分布式中的事务

在单服务的时候运行业务的时候,进行事务管理通常是比较简单的。但是在分布式环境下,事务被分散到各个实例中,每次执行事务都需要保证各个系统的事务完成。但是受限于不同实例服务器的稳定性,网络的可靠性,完成一项事务会变得非常麻烦。

CAP理论

说道分布式事务是绕不开CAP理论。所谓的CAP指的是一致性(Consistency),可用性(Availability),分区容忍性(Partition tolerance)。

一致性(Consistency)

每次读取要么是最新的数据,要么是一个错误

可用性(Availability)

客户端在任何时刻的读写操作都能在限定的延迟内完成的,即每次请求都能获得一个响应,但不保证是最新的数据;

分区容忍性(Partition tolerance)

分布式系统中,节点组成的网络都是连通的。但是可能存在一些文档,导致连通的节点中断联系了。整个连通的网络成了分散的几块区域。数据被分割在不同的区域里面。这个时候任何一个事务都无法获取完整的数据。这时分区就是无法容忍的。在大规模分布式系统中,网络分区现象是必然会发生的,系统应该能保证在这种情况下可以正常工作。

对CAP的满足

实际上发现很难去完全实现CAP的所有要求。我们一般只能实现两项。或者实现一项,均衡的实现其他两项内容。

CA (Consistency + Availability)

关注一致性和可用性。这种它需要非常严格的全体一致的协议。CA 系统不能容忍网络错误或节点错误,一旦出现这样的问题,整个系统就会拒绝写请求,因为它并不知道对面的那个结点是否挂掉了。常用的就是2PC。。

CP (consistency + partition tolerance)

关注一致性和分区容忍性,它关注的是系统里大多数人的一致性协议,这样的系统只需要保证大多数结点数据一致,而少数的结点会在没有同步到最新版本的数据时变成不可用的状态。比如:Paxos 算法。

AP (availability + partition tolerance)

关心可用性和分区容忍性。因此,这样的系统不能达成一致性,需要给出数据冲突,给出数据冲突就需要维护数据版本。Dynamo 就是这样的系统。

根据CAP的理论,我们要么实现AP,要么CP,要么AC。假如一个分布式系统中不存在副本,则数据一定是强一致的,不可能发现数据不一致。但是此时发生了数据分区系统则一定会拒绝访问的。但是我们提供备份避免服务分区,则在大规模分布式中一定会存在数据不一致的情况。这个时候可用性和一致性发生了冲突。我们需要在强一致性弱可用性和高可用性容忍弱一致性做出选择。为了解决分布式系统的一致性问题,常出现了二阶段提交协议(2PC),三阶段提交协议(3PC)这些解决方案

2PC

2PC:Two-phase Commit,二阶段提交协议。是一种比较常用的分布式事务解决方案。其主要的作用是在分布式事务中,要么所有参与者都提交事务,要么都取消事务,保证参与者的一致性。

二阶段提交

2PC将事务的提交分成了两个过程:1. 准备阶段 2. 提交阶段

准备阶段

  1. 在准备阶段,协调者向所有参与者发送一个vote request确认参与者准备好开始事务。
  2. 参与者在收到请求后,各参与者节点准备事务操作。如果准备好则返回一个“同意”标识,如果没有准备好则返回一个“中止”标识。

提交阶段

  1. 协调者收到所有参与者的反馈后,如果所有参与者认为可以提交,则提交事务,否则就中断事务。并将处理结果发送给参与者。
  2. 作为参与者,如果收到提交消息则提交本地事务,否则取消本地事务。

此时事务可以提交

2PC存在的问题

既然是分布式事务,在分布式环境下我们不得不考虑一些特殊情况下出现不同的错误要如何解决

同步阻塞

整个事务过程中,所有参与者都处于阻塞状态

协调者宕机

协调者是整个事务的核心,当协调者宕机,参与者会被锁定

未完全执行事务

在第二阶段如果协调者和参与者都宕机,即使后续都回复了,假如此时参与者已经执行了事务操作,但是这时候无论协调者还是他自己都无法知道。协调者备份会将执行事务的指令再次发送给参与者,会产生错误数据。

在实际中也尝试对上面的问题进行修复:针对协调者宕机,提供了协调者备份。当协调者宕机后,备份会启动接替协调者的职责。但是对于第二阶段部分参与者或者参与者和协调者都宕机后存在数据不一致,以及同步阻塞的问题还是无法解决。

3PC

为了解决2PC在数据不一致上出现的问题,于是提出了2PC的升级版3PC。

在3PC中事务被分为了三个阶段:CanCommit、PreCommit、doCommit。整个阶段使用维基百科可以描述为下面内容

三阶段提交

CanCommit阶段(提交询问)

  1. 协调者想各个参与者询问是否可以进行事务提交,并收集响应结果
  2. 参与者向协调者反馈事务内容。此时收集到的反馈为同意则进入预备阶段,否则中断事务

PreCommit阶段(预提交)

  1. 事务预提交,如果询问事务的返回都是同意,协调者则向各个参与者发送预提交请求,并进入prepared 阶段
  2. 参与者接收到预提交请求后,执行事务操作,并保存Undo 和 Redo 信息记录到事务日记中
  3. 各个参与者向协调者反馈事务执行的响应,如果完成事务操作,则反馈给协调者。同时等待最终命令,执行提交或者终止
  4. 如果协调者收到预提交的响应为拒绝或者超时,则执行中断事务操作,向各个参与者反馈事务执行响应。
  5. 参与者收到中断事务的响应,或者等待超时,都会主动中断事务。

doCommit阶段(最终提交)

  1. 协调者收到各个参与者响应,则从预提交进入提交阶段,并向参与者发送提交请求
  2. 参与者收到提交指令,正式提交事务
  3. 参与者向服务器反馈最终提交结果
  4. 协调者收到所有反馈信息,完成事务
  5. 此时假如有协调者没有收到反馈,则发送中断事务指令
  6. 参与者收到拒绝指令后,利用日志开始进行事务回滚。
  7. 参与者反馈回滚结果
  8. 参与者接收反馈,中断事务

上面内容可以用下面的来表示

和2PC相比,3PC对于协调者和参与者都设置了超时时间,这样避免了参与者在长时间无法和协调者通讯的时候,无法释放资源。因为参与者自身拥有超时机制会在超时后,自动进行中断从而进行释放资源。

通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的。当然3PC也只是多了一个阶段的保护,也并没有完全解决数据不一致的问题。

比较疑惑的地方

有一些问题感觉描述的很混乱。根据wiki上的图以及描述

If, after a cohort member receives a preCommit message, the coordinator fails or times out, the cohort member goes forward with the commit.
分布式事务2PC和3PC

预提交超时的时候会进行提交操作,但是网上很多博客的说法却是

PreCommit阶段:无论收到协调者发出的abort请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

这里表述有点模糊,按照wiki说法在预提交后没有收到最终确认则参与者直接提交。但是国内很多博客上描述是等待协调者请求过程中出现超时则直接中断事务,我这里我看了国内很多人总结2PC和3PC中的一句话

协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作

,所以我认为在预提交没有获得协调者反馈的情况下应该是直接提交的。但这是个我个人的想法,所以我这里标注了存疑。希望有了解的朋友解答下。

继续阅读