事务-03丨2PC和3PC

Posted by jiefang on January 13, 2021

2PC和3PC

2PC

2PC二阶段提交协议(Two Phase Commitment Protocol),是强一致、中心化的原子提交协议2PC将事务的提交过程分为:准备阶段和提交阶段。事务的发起者称协调者,事务的执行者称参与者

阶段1:准备阶段

  • 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。
  • 各参与者执行事务操作,但不提交事务。
  • 如参与者执行成功,给协调者反馈YES,即可以提交;如执行失败,给协调者反馈NO,即不可提交。

阶段2:提交阶段

阶段分两种情况:所有参与者均反馈YES、或任何一个参与者反馈NO。

  • 所有参与者均反馈YES时,即提交事务。
  • 任何一个参与者反馈NO时,即中断事务。

提交事务

  • 协调者向所有参与者发出正式提交事务的请求(即Commit请求)。
  • 参与者执行Commit请求,并释放整个事务期间占用的资源。
  • 各参与者向协调者反馈Ack完成的消息。
  • 协调者收到所有参与者反馈的Ack消息后,即完成事务提交。

中断事务

  • 协调者向所有参与者发出回滚请求(即Rollback请求)。
  • 参与者使用阶段1中的Undo信息执行回滚操作,并释放整个事务期间占用的资源。
  • 各参与者向协调者反馈Ack完成的消息。
  • 协调者收到所有参与者反馈的Ack消息后,即完成事务中断。

缺点

  • 性能问题:无论是在第一阶段的过程中,还是在第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点准备完毕,事务 协调者 才会通知进行全局提交,参与者 进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大
  • 单节点故障:由于协调者的重要性,一旦 协调者 发生故障。参与者 会一直阻塞下去。尤其在第二阶段,协调者 发生故障,那么所有的 参与者 还都处于锁定事务资源的状态中,而无法继续完成事务操作。
  • 数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。

故障场景

  • 协调者正常,参与者宕机:

    • 由于 协调者 无法收集到所有 参与者 的反馈,会陷入阻塞情况;
    • 解决方案:引入超时机制,如果协调者在超过指定的时间还没有收到参与者的反馈,事务就失败,向所有节点发送终止事务请求。
  • 协调者宕机,参与者正常:

    • 无论处于哪个阶段,由于协调者宕机,无法发送提交请求,所有处于执行了操作但是未提交状态的参与者都会陷入阻塞情况。
    • 解决方案:引入协调者备份,同时协调者需记录操作日志.当检测到协调者宕机一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态。
  • 协调者和参与者都宕机:

    • 第一阶段: 因为第一阶段,所有参与者都没有真正执行commit,所以只需重新在剩余的参与者中重新选出一个协调者,新的协调者在重新执行第一阶段和第二阶段就可以了。
    • 第二阶段 并且 挂了的参与者在挂掉之前没有收到协调者的指令:这种情形下,新的协调者重新执行第一阶段和第二阶段操作。

    • 第二阶段 并且 有部分参与者已经执行完commit操作: 2PC 无法解决这个问题。

3PC

三阶段提交(Three-phase commit),是二阶段提交(2PC)的改进版本,主要是为了解决两阶段提交协议的阻塞问题,2PC存在的问题是当协作者崩溃时,参与者不能做出最后的选择。3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommitPreCommitDoCommit三个阶段。

改动点

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

CanCommit阶段

之前2PC的一阶段是本地事务执行结束后,最后不Commit,等其它服务都执行结束并返回Yes,由协调者发生commit才真正执行commit。而这里的CanCommit指的是 尝试获取数据库状态 如果可以,就返回Yes。

  • 事务询问协调者参与者 发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待 参与者 的响应。
  • 响应反馈参与者 接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态,否则反馈No。

PreCommit阶段

如果所有的参与者都返回Yes的话,那么就会进入PreCommit阶段进行事务预提交。PreCommit阶段 跟2PC的准备阶段差不多的,只不过这里 协调者和参与者都引入了超时机制 (2PC中只有协调者可以超时,参与者没有超时机制)。

DoCommit阶段

跟2PC的提交阶段类似。

相对2PC的优化

  • 3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制:避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。
  • 通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较于2PC而言,多设置了一个CanCommit阶段保证了在最后提交阶段之前各参与节点的状态是一致的。

XA事务

XA事务是基于二阶段提交协议实现的。

  • XA是由X/Open组织提出的分布式事务的规范。 XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口。主流的关系型 数据库产品都是实现了XA接口的。
  • XA接口是双向的系统接口,在事务管理器 (TM)以及一个或多个资源管理器(RM)之 间形成通信桥梁。
  • XA之所以需要引入事务管理器是因为,在分布 式系统中,从理论上讲两台机器理论上无法达 到一致的状态,需要引入一个单点进行协调。
  • 由全局事务管理器管理和协调的事务,可以跨 越多个资源(如数据库或JMS队列)和进程。 全局事务管理器一般使用 XA 二阶段提交协议 与数据库进行交互。

JTA事务模型

Java事务API(JTA:Java Transaction API)和它的同胞Java事务服务(JTS:Java Transaction Service),为J2EE平台提供了分布式事务服务(distributed transaction)的能力。 某种程度上,可以认为JTA规范是XA规范的Java版,其把XA规范中规定的DTP模型交互接口抽象成Java接口中的方法,并规定每个方法要实现什么样的功能。

在DTP模型中,规定了模型的五个组成元素:应用程序(Application)、资源管理器(Resource Manager)、事务管理器(Transaction Manager)、通信资源管理器(Communication Resource Manager)、 通信协议(Communication Protocol)。

而在JTA规范中,模型中又多了一个元素Application Server,如下所示:

JTA规范–接口定义

  • ​ javax.transaction.Status:事务状态,这个接口主要是定义一些表示事务状态的常量,此接口无需实现
  • ​ javax.transaction.Synchronization:同步
  • ​ javax.transaction.Transaction:事务
  • ​ javax.transaction.TransactionManager:事务管理器
  • ​ javax.transaction.UserTransaction:用于声明一个分布式事务
  • ​ javax.transaction.TransactionSynchronizationRegistry:事务同步注册
  • ​ javax.transaction.xa.XAResource:定义RM提供给TM操作的接口
  • ​ javax.transaction.xa.Xid:事务id

TM供应商:

​ 实现UserTransaction、TransactionManager、Transaction、TransactionSynchronizationRegistry、Synchronization、Xid接口,通过与XAResource接口交互来实现分布式事务。此外,TM厂商如果要支持跨应用的分布式事务,那么还要实现JTS规范定义的接口。

常见的TM提供者包括我们前面提到的application server,包括:jboss、ejb server、weblogic等,以及一些以第三方类库形式提供事务管理器功能的jotm、Atomikos。

RM供应商:

XAResource接口需要由资源管理器者来实现,XAResource接口中定义了一些方法,这些方法将会被TM进行调用,如:

  • start方法:开启事务分支
  • end方法:结束事务分支
  • prepare方法:准备提交
  • commit方法:提交
  • rollback方法:回滚
  • recover方法:列出所有处于PREPARED状态的事务分支