刚性事务
1.DTP模型
X/Open组织介绍
X/OPEN是一个组织(现在的open group)X/Open国际联盟有限公司是一个欧洲基金会,它的建立是为了向UNIX环境提供标准。它主要的目标是促进对UNIX语言、接口、网络和应用的开放式系统协议的制定。它还促进在不同的UNIX环境之间的应用程序的互操作性,以及支持对电气电子工程师协会(IEEE)对UNIX的可移植操作系统接口(POSIX)规范
主要成员
DTP模型介绍
X/Open DTP(Distributed Transaction Process)是一个分布式事务模型。这个模型主要使用了两段提交(2PC - Two-Phase-Commit)来保证分布式事务的完整性
这套标准主要定义了实现分布式事务的规范和API,具体的实现则交给相应的⼚商来实现
X/Open提供的参考文档
- DTP 参考模型:http://pubs.opengroup.org/onlinepubs/9294999599/toc.pdf
- DTP XA规范:http://pubs.opengroup.org/onlinepubs/009680699/toc.pdf
相关概念
- 事务:⼀个事务就是⼀个完整的⼯作单元,具备ACID特性
- 全局事务:由事务管理器管理的事务,能够⼀次性操作多个资源管理器
- 分⽀事务:由事务管理器管理的全局事务中,每个资源管理器中独⽴执⾏的事务
- 控制线程:执⾏全局事务的线程,这个线程⽤来关联应⽤程序、事务管理器和资源管理器 三者之间的关系,也就是表示全局事务和分⽀事务的关系,通常称为事务上下⽂环境
核心组件
- AP:应用程序(Application Program)用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作,可以理解为参与DTP分布式事务模型的应⽤程序
- RM:资源管理器(Resource Manager)可以理解为数据库管理系统或消息服务管理器。应⽤程序可以通过资源管理器对相应的资源进⾏有效的控制。相应的资源需要实现XA定义的接⼝
- TM:事务管理器(Transaction Manager)负责分配事务唯一标识,监控事务的执行进度,并负责全局事务的提交、回滚等,为应⽤程序提供编程接⼝
- CRM(不重要):通信资源管理器(Communication Resource Manager)控制一个TM域(TM domain)内或者跨TM域的分布式应用之间的通信
- CP(不重要):通信协议(Communication Protocol)提供CRM提供的分布式应用节点之间的底层通信服务
注意
- DTP模型定义了XA接⼝,TM和RM能够通过XA接⼝进⾏双向通信。TM控制着全局事务,管理事务的⽣命周期并协调资源。 RM控制和管理实际的资源
- 一个DTP模型实例,至少有3个组成部分:AP、RM、TM,也叫做DTP本地模型实例
执行流程
2.两阶段提交协议(2PC)
为什么会诞生2PC和3PC
在分布式系统中,会有多个机器节点,每一个机器节点虽然能够明确地知道自己在进行事务操作过程中的结果是成功或失败,但无法直接获取到其他分布式节点的操作结果,因此,当一个事务操作需要跨越多个分布式节点的时候,为了保证事务处理的ACID特性,就需要引入一个“协调者”的组件来统一调度所有分布式节点的执行逻辑,这些被调度的节点则称为“参与者”,协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正进行提交。基于这个思想,就衍生了二阶段提交和三阶段提交两种协议
介绍
2PC,是Two Phase Commit缩写,即两阶段提交。准备阶段(Prepare phase)、提交阶段(commitphase)。是计算机网络,尤其是数据库领域中,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性和一致性而设计的一种算法,通常,2PC也被认为是一种一致性协议,用来保证分布式系统数据的一致性,目前,绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事务处理的,利用该协议能够非常方便地完成所有分布式事务参与者的协调,统一决定事务的提交和回滚,从而能够有效地保证分布式数据的一致性
角色
- 协调者(Coordinater):事务管理器(TM)
- 参与者(participants):资源管理器(RM)
整体执行流程
Prepare阶段
- 事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交请求,并开始等待各参与者的响应
- 执行事务但不提交:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中
- 参与者向协调者反馈事务询问的响应:如果参与者成功执行了事务操作,那么就反馈给协调者Yes的响应,表示事务可以执行。如果参与者没有成功执行事务,就返回No给协调者,表示事务不可执行。还有可能参与者根本没接收到事务询问所以没有响应
Commit阶段之执行事务
- 发送提交请求:协调者向所有参与者发出commit请求
- 事务提交:参与者收到commit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源
- 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack信息
- 完成事务:协调者接收到所有参与者反馈的Ack信息后,完成事务
Commit阶段之回滚事务
- 发送回滚请求:协调者向所有参与者发出Rollback请求
- 事务回滚:参与者接收到Rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源
- 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack信息
- 中断事务:协调者接收到所有参与者反馈的Ack信息后,完成事务中断
问题
- 同步阻塞问题:在二阶段提交的执行过程中,所有参与该事务操作的逻辑都处于阻塞状态,也就是说,各个参与者在等待其他参与者响应的过程中,无法进行其他操作。这种同步阻塞极大的限制了分布式系统的性能
- 单点故障问题:协调者在整个二阶段提交过程中很重要,如果协调者在提交阶段出现问题,那么整个流程将无法运转,更重要的是,其他参与者将会处于一直锁定事务资源的状态中,而无法继续完成事务操作
- 数据不⼀致问题:假设当协调者向所有的参与者发送commit请求之后,发生了局部网络异常或者是协调者在尚未发送完所有commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了commit请求。这将导致严重的数据不一致问题
- 过于保守:如果在事务询问中,参与者出现故障而导致协调者始终无法获取到所有参与者的响应信息的话,这时协调者只能依靠其自身的超时机制来判断是否需要中断事务,显示,这种策略过于保守。换句话说,二阶段提交协议没有设计较为完善的容错机制,任意一个节点失败都会导致整个事务的失败
使用场景
- 单个服务操作多个数据库
- 并发不高
3.三阶段提交协议(3PC)
介绍
3PC,全称“three phase commit”,是2PC的改进版,将2PC的“提交事务请求”过程一分为二,共形成了由CanCommit,PreCommit和doCommit三个阶段组成的事务处理协议
相比于二阶段提交改进的地方
- 在协调者与参与者都引入了超时机制(在2PC中,只有协调者拥有超时机制,既如果在一定时间内没有收到参与者的消息则默认失效)
- 当进入第三阶段,由于网络超时等原因,虽然参与者没有收到commit或abort响应,但是它有理由相信,成功提交的几率很大
整体流程
CanCommit阶段
- 事务询问:协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应
- 各参与者向协调者反馈事务询问的响应:参与者在接收到来自协调者的包含了事务内容的canCommit请求后,正常情况下,如果自身认为可以顺利执行事务,则反馈Yes响应,并进入预备状态,否则反馈No响应
PreCommit阶段之执行事务预提交
- 发送预提交请求:协调者向所有参与者节点发出preCommit请求,并进入prepared阶段
- 事务预提交:参与者接受到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中
- 各参与者向协调者反馈事务执行的结果:若参与者成功执行了事务操作,那么反馈Ack,同时等待最终的指令:提交或终止
PreCommit阶段之执行中断事务
- 发送中断请求:协调者向所有参与者发出abort请求
- 中断事务:无论是收到来自协调者的abort请求或者等待协调者请求过程中超时,参与者都会中断事务
DoCommit阶段之执行事务提交
- 发送提交请求:进入这一阶段,假设协调者处于正常工作状态,并且它接收到了来自所有参与者的Ack响应,那么它将从预提交状态转换为提交状态,并向所有的参与者发送doCommit请求
- 事务提交:参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行过程中占用的事务资源
- 反馈事务提交结果:参与者在完成事务提交后,向协调者发送Ack响应
- 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务
DoCommit阶段之中断事务
- 发送中断请求:协调者向所有的参与者节点发送abort请求
- 事务回滚:参与者接收到abort请求后,会根据记录的Undo信息来执行事务回滚,并在完成回滚之后释放整个事务执行期间占用的资源
- 反馈事务回滚结果:参与者在完成事务回滚后,向协调者发送Ack信息
- 中断事务:协调者接收到所有参与者反馈的Ack信息后,中断事务
进入阶段三可能会出现两种故障
- 协调者出现问题
- 协调者和参与者之间的网络故障
如果出现了任意一种情况,最终都会导致参与者无法收到doCommit请求或者abort请求,针对这种情况,参与者都会在等待超时之后,继续进行事务提交
优缺点
优点
相比较于2PC,最大的优点就是降低了参与者的阻塞范围(第一个阶段是不阻塞的),其次能够在单点故障后继续达成一致(2PC在提交阶段会出现此问题,而3PC会根据协调者的状态进行回滚或提交)
缺点
阶段三发送abort请求的时候,出现了网络分区(有的节点收到了abort请求有的没有),此时协调者所在的节点和参与者所在的节点无法进行正常的网络通信,那么参与者等待超时后,会进行事务的提交,这必然会出现分布式数据不一致的问题
4.XA协议
作用
XA规范的最主要的作用是,就是定义了RM-TM的交互接口,下图更加清晰了演示了XA规范在DTP模型中发挥作用的位置,从下图中可以看出来,XA仅仅出现在RM和TM的连线上
官方说法
- XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准
- XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口。 XA规范 的目的是允许的多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效
- XA 规范 最终完成两阶段提交(2PC,Two-Phase Commit)协议来保证所有资源同时提交或回滚任何特定的事务
- XA 规范 在上世纪 90 年代初就被提出。目前,几乎所有主流的数据库都对 XA 规范 提供了支持
常见的误解
有些人可能会误认为两阶段提交协议是在XA规范中提出来的。事实上: 两阶段协议是在OSI TP标准中提出的;在DTP参考模型中,指定了全局事务的提交要使用two-phase commit协议;而XA规范只是定义了两阶段提交协议中需要使用到的接口,也就是上述提到的RM-TM交互的接口,因为两阶段提交过程中的参与方,只有TM和RMs
介绍
XA规范(XA Specification) 是X/OPEN 提出的分布式事务处理规范。XA则规范了TM与RM之间的通信接口,在TM与多个RM之间形成一个双向通信桥梁,从而在多个数据库资源下保证ACID四个特性。目前知名的数据库,如Oracle, DB2,mysql等,都是实现了XA接口的,都可以作为RM
XA是数据库的分布式事务,强一致性,在整个过程中,数据一直锁住状态,即从prepare到commit、rollback的整个过程中,TM一直把持这数据库的锁,如果有其他人要修改数据库的该条数据,就必须等待锁的释放,存在长事务风险
准确讲XA是一个规范、协议,它只是定义了一系列的接口,只是目前大多数实现XA的都是数据库或者MQ,所以提起XA往往多指基于资源层的底层分布式事务解决方案。其实现在也有些数据分片框架或者中间件也支持XA协议,毕竟它的兼容性、普遍性更好
规定中RM提供的接口
- xa_open,xa_close:建立和关闭与资源管理器的连接
- xa_start,xa_end:开始和结束一个本地事务
- xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务
- xa_recover:回滚一个已进行预提交的事务
- ax_开头的函数使资源管理器可以动态地在事务管理器中进行注册,并可以对XID(TRANSACTION IDS)进行操作
- ax_reg,ax_unreg:允许一个资源管理器在一个TMS(TRANSACTION MANAGER SERVER)中动态注册或撤消注册
XA各个阶段的处理流程
支持XA协议的常见分布式事务框架
- Atomikos
- Seata
5.JTA规范
规范下载地址
https://download.oracle.com/otn-pub/jcp/jta-1.1-spec-oth-JSpec/jta-1_1-spec.pdf?AuthParam=1678249590_c72c2e509ad94b22138dce2f61f66b12
介绍
它是JavaEE的13个规范之一
Java事务API/Java平台事务规范(JTA:Java Transaction API)和它的同胞Java事务服务(JTS:Java Transaction Service),为J2EE平台提供了分布式事务服务(distributed transaction)的能力。 某种程度上,可以认为JTA规范是XA规范的Java版,其把XA规范中规定的DTP模型交互接口抽象成Java接口中的方法,并规定每个方法要实现什么样的功能
JTA是基于XA架构上建模的,在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即JTS)实现。像很多其他的java规范一样,JTA仅仅定义了接口
JTA的常见实现
JTA规范规定,事务管理器的功能应该由application server提供,如EJB Server。一些常见的其他web容器,如:jboss、weblogic、websphere等,都可以作为application server,这些web容器都实现了JTA规范。特别需要注意的是,并不是所有的web容器都实现了JTA规范,如tomcat并没有实现JTA规范,因此并不能提供事务管理器的功能
- J2EE容器所提供的JTA实现(JBoss)
- 独立的JTA实现:如JOTM,Atomikos,这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用
接口定义
依赖
JTA是java扩展包,在应用中需要额外引入相应的jar包依赖
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
</dependency>
重要类含义
JTA规范中定义的这些接口,并不需要应用程序的开发人员去实现。而是由各个厂商去实现,根据在DTP模型中扮演的不同角色,需要实现不同的接口
- 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状态的事务分支
- 一些RM提供者,可能也会提供自己的Xid接口的实现
此外,不同的资源管理器有一些各自的特定接口要实现:
- 如JDBC2.0规范定义支持分布式事务的jdbc driver需要实现:javax.sql.XAConnection、javax.sql.XADataSource接口
- JMS1.0规范规定支持分布式事务的JMS厂商,需要实现javax.jms.XAConnection、javax.jms.XASession接口
注意
作为DTP模型中Application开发者的我们,并不需要去实现任何JTA规范中定义的接口,只需要使用TM提供的UserTransaction实现,来声明、提交、回滚一个分布式事务即可
使用案例
需要注意的是,在分布式事务中,当我们需要提交或者回滚一个事务时,不应该再使用Connection接口提供的commit和rollback方法。而是应该使用UserTransaction接口的commit接口和rollback接口替代。
另外,在本案例中,我们并没有说明UserTransaction实例是如何构建的,这是由事务管理器(TM)实现者提供的,而目前我们还没有接触过任何事务管理器
UserTransaction userTransaction=...
try{
//开启分布式事务
userTransaction.begin();
//执行事务分支1
conn1 = db1.getConnection();
ps1= conn1.prepareStatement("INSERT into user(name,age) VALUES ('tianshouzhi',23)");
ps1.executeUpdate();
//执行事务分支2
conn2 = db2.getConnection();
ps2 = conn2.prepareStatement("INSERT into user(name,age) VALUES ('tianshouzhi',23)");
ps2.executeUpdate();
//提交,两阶段提交发生在这个方法内部
userTransaction.commit();
}catch (Exception e){
try {
userTransaction.rollback();//回滚
} catch (SystemException ignore) {
}
}
6.JTS规范
事务是编程中必不可少的一项内容,基于此,为了规范事务开发,Java增加了关于事务的规范,即JTA和JTS
JTA定义了一套接口,其中约定了几种主要的角色:TransactionManager、UserTransaction、Transaction、XAResource,并定义了这些角色之间需要遵守的规范,如Transaction的委托给TransactionManager等
JTS也是一组规范,上面提到JTA中需要角色之间的交互,那应该如何交互?JTS就是约定了交互细节的规范
总体上来说JTA更多的是从框架的角度来约定程序角色的接口,而JTS则是从具体实现的角度来约定程序角色之间的接口,两者各司其职
7.Atomikos分布式事务
Atomikos公司旗下有两款著名的分布事务产品:
- TransactionEssentials:开源的免费产品
- ExtremeTransactions:商业版,需要收费
这两个产品的关系如下图所示:
XA的主要限制
- 必须要拿到所有数据源,而且数据源还要支持XA协议。目前MySQL中只有InnoDB存储引擎支持XA协议
- 性能比较差,要把所有涉及到的数据都要锁定,是强一致性的,会产生长事务