分布式事务的目的是保证分布式系统中的多个参与方的数据能够保证一致性。即所有参与者,在一次写操作过程中要么都成功,要么都失败。
如果想要实现强一致性,那么就一定要引入一个协调者,通过协调者来协调所有参与者来进行提交或者回滚。所以,这类方案包含基于 XA 规范的二阶段及三阶段提交、以及支持2阶段提交。
XA 规范
X/Open 组织(即现在的 Open Group)定义了分布式事务处理模型。模型中主要包括应用程序(AP)、事务管理(TM)、资源管理器(RM)、通信资源管理器(CRM)等四个角色。
一般,常见的事务管理器(TM)是交易中间件,常见的资源管理器(RM)是数据库,常见的通信资源管理器(CRM )消息中间件。
通常把一个数据库内部的事务处理,如对多个表的操作,作为本地事务看待。数据库的事务处理对象是本地事务分布式事务处理的对象是全局事务,
所谓全局事务,是指分布式事务处理环境中,多个数据库可能需要共同完成一个工作,这个工作即是一个全局事务,例如,一个事务中可能更新几个不同的数据库。对数据库的操作发生在系统的各处但必须全部被提交或回滚。此时一个数据库对自己内部所做操作的提交不仅依赖本身操作是否成功,还要依赖与全局事务相关的其它数据库的操作是否成功,如果任一数据库的任一操作失败,则参与此事务的所有数据库所做的所有操作都必须回滚。
XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA 接口函数由数据库厂商提供。
阶提交协议和三阶提交协议就是根据这一思想行生出来的。可以说二阶段提交其实就是实现 XA 分布式事务的关键。
2 PC
所谓的两个阶段是指:第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)
利用协调者 (Coordinator) 对参与者 (Cohort) 进行统一的事务提交和回滚。
第一阶段:准备阶段(投票阶段)
- 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者(Cohort)节点的响应。
- 参与者节点执行事务操作
- 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。
第二阶段:提交阶段(执行阶段)
协调者会根据参与者的反馈情况来决定是否可以进行事务提交操作,可分为事务提交以及事务中断两种情况 。
一旦事务执行之后,在没有执行 commit 或者 rolback 之前,资源是被锁定的。这会造成阻塞。
事务提交
当收到所有的参与者的反馈过后,如果都是 yes ,那么进行事务的提交。
事务中断
当收到所有的参与者的反馈过后,如果有 no ,或者有超时未响应的就会发送 Rollback 请求。
2PC 存在的问题
二阶段提交中,最重要的问题是可能会带来数据不一致的问题,除此之外,还存在同步阻塞以及单点故障的问题,
首先看为什么会发生同步阻塞和单点故障的问题:
同步阻塞问题。
执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
单点故障。
由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
作为一个分布式的一致性协议,我们主要关注他可能带来的一致性问题的。2PC在执行过程中可能发生协调者或者参与者突然宕机的情况,在不同时期宕机可能有不同的现象。
情况一:协调者挂了,参与者没挂,
重新找一个协调者,询问所有参与者的最后那条事务的执行情况,那也就知道如何操作了
情况二:参与者挂了,协调者没挂
这种情况其实也比较好解决。如果参与者挂了。那么之后的事情有两种情况:
- 第一个是挂了就挂了,没有再恢复。那就挂了呗,反正不会导致数据一致性问题。
- 第二个是挂了之后又恢复了,这时如果他有未执行完的事务操作,直接取消掉,然后询问协调者目前我应该怎么做,协调者就会比对自己的事务执行记录和该参与者的事务执行记录,告诉他应该怎么做来保持数据的一致性。
情况三:参与者和协调者都挂了,
- 协调者和参与者在第一阶段挂了。
- 由于这时还没有执行commit操作,新选出来的协调者可以询问各个参与者的情况,再决定是进行commit还是rollback。因为还没有commit,所以不会导致数据一致性问题。
- 第二阶段协调者和参与者挂了,挂了的这个参与者在挂之前并没有接收到协调者的指令,或者接收到指令之后还没来的及做commit或者rollback操作。
- 这种情况下,当新的协调者被选出来之后,他同样是询问所有的参与者的情况。只要有机器执行了**abort(rolback) **操作或者第一阶段返回的信息是No的话,那就直接执行 rolback操作。
- 如果没有人执行 abort 操作,但是有机器执行了 commit 操作,那么就直接执行 commit 操作。
- 这样,当挂掉的参与者恢复之后,只要按照协调者的指示进行事务的 commit 还是 rolback 操作就可以了。因为挂掉的机器并没有做 commit 或者 rollback 操作,而没有挂掉的机器们和新的协调者又执行了同样的操作,那么这种情况不会导致数据不一致现象。
- 第二阶段协调者和参与者挂了,挂了的这个参与者在挂之前已经执行了操作。但是由于他挂了,没有人知道他
执行了什么操作。
- 这种情况下,新的协调者被选出来之后,如果他想负起协调者的责任的话他就只能按照之前那种情况来执行 commit 或者 rolback 操作。这样新的协调者和所有没挂掉的参与者就保持了数据的一致性,我们假定他们执行了 commit。但是,这个时候,那个挂掉的参与者恢复了怎么办,因为他之前已经执行完了之前的事务,如果他执行的是 commit 那还好,和其他的机器保持一致了,万一他执行的是 rolback 操作那?这不就导致数据的不一致性了么?虽然这个时候可以再通过手段让他和协调者通信,再想办法把数据搞成一致的,但是,这段时间内他的数据状态已经是不一致的了!
所以,2PC协议中,如果出现协调者和参与者都挂了的情况,有可能导致数据不一致。
3 PC
3PC为了改进 2PC 中出现的同步阻塞、单点问题、脑裂以及保守的容错机制缺陷提出的三阶段提交协议,分为CanCommit(事务询问)、PreCommit(事务执行)、DoCommit(事务提交)三个阶段三阶段进行事务处理协议。如下图:
阶段一:CanCommit
- 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
- 协调者向参与者发送 commit 请求,参与者如果可以提交就返回Yes响应进入预备状态,否则返回 No 响应。
阶段二:PreCommit
协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。根据响应情况,有以下两种可能执行提交和中断事务:
执行提交:
协调者从所有的参与者获得的反馈都是Yes响应,那么就会进行事务的预执行:
- 发送预提交请求::协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段。
- 事务预提交:参与者接收到 PreCommit 请求后,会执行事务操作,
- 响应反馈:如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。
中断事务:
有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,Coordinator 都没有接到参与者的响应,那么就中断事务:
- 发送中断请求:协调者向所有参与者发送 abort**(rolback) ** 请求。
- 中断事务:参与者收到来自协调者的 abort**(rolback) **请求之后(或超时之后,仍未收到参与者的请求),执行事务的中断。
阶段三:DoCommit
该阶段进行真正的事务提交,也可以分为以下两种情况执行提交和中断事务:
执行提交
- 发送提交请求:协调者接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
- 事务提交:参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
- 响应反馈:事务提交完之后,向协调者发送 ACK 响应。
- 完成事务:协调者接收到所有参与者的 ACK 响应之后,完成事务。
中断事务:
协调者(Coordinator)正常,接收到参与者(Cohort)发送的反馈No响应,或者没有收到到Ack响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。
- 发送中断请求:协调者向所有参与者节点发送 abort 请求;
- 事务回滚:参与者接收到abort请求后,进行事务回滚操作,并在回滚完成后释放整个事务执行期间占用的资源。
- 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送 Ack 消息。
- 中断事务:协调者接收所有参与者的反馈Ack消息后,中断事务。
优缺点
- 优点: 降低参与者阻塞范围,并能够在出现单点故障后继续达成一致
- 缺点: 引入preCommit阶段,在这个阶段如果出现网络分区,协调者无法与参与者正常通信,参与者依然会进行事务提交,造成数据不一致。
总结
无论是二阶段提交还是三阶段提交都从不同程度地解决了分布式数据一致性问题,使用范围非常广泛,但都无法彻底解决分布式的一致性问题。还有一种一致性协议 Raft、ZAB、Paxos 算法,解决了无限期等待问题,也解决了“脑裂”问题。