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的准备阶段再次一分为二,这样三阶段提交就有CanCommit
、PreCommit
、DoCommit
三个阶段。
改动点
- 引入超时机制。同时在协调者和参与者中都引入超时机制。
- 新增一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
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状态的事务分支