XA模型 或者 X/Open DTP模型
X/Open DTP(Distributed Transaction Process) 是一个分布式事务模型。这个模型主要使用了两段提交(2PC - Two-Phase-Commit)来保证分布式事务的完整性。
在X/Open DTP(Distributed Transaction Process)模型里面,有三个角色:
AP: Application,应用程序。也就是业务层。哪些操作属于一个事务,就是AP定义的。
TM: Transaction Manager,事务管理器。接收AP的事务请求,对全局事务进行管理,管理事务分支状态,协调RM的处理,通知RM哪些操作属于哪些全局事务以及事务分支等等。这个也是整个事务调度模型的核心部分。
RM:Resource Manager,资源管理器。一般是数据库,也可以是其他的资源管理器,如消息队列(如JMS数据源),文件系统等。
XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。
XA之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)
XA是数据库的分布式事务,强一致性,在整个过程中,数据一张锁住状态,即从prepare到commit、rollback的整个过程中,TM一直把持折数据库的锁,如果有其他人要修改数据库的该条数据,就必须等待锁的释放,存在长事务风险。
以下的函数使事务管理器可以对资源管理器进行的操作:
1)xa_open,xa_close:建立和关闭与资源管理器的连接。
2)xa_start,xa_end:开始和结束一个本地事务。
3)xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。
4)xa_recover:回滚一个已进行预提交的事务。
5)ax_开头的函数使资源管理器可以动态地在事务管理器中进行注册,并可以对XID(TRANSACTION IDS)进行操作。
6)ax_reg,ax_unreg;允许一个资源管理器在一个TMS(TRANSACTION MANAGER SERVER)中动态注册或撤消注册。
XA各个阶段的处理流程
XA协议的实现
2PC/3PC协议
两阶段提交(2PC)协议是XA规范定义的 数据一致性协议。
三阶段提交(3PC)协议对 2PC协议的一种扩展。
成熟的中间件
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式。
Jta规范
作为java平台上事务规范 JTA(Java Transaction API)也定义了对XA事务的支持,实际上,JTA是基于XA架构上建模的,在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即JTS)实现。像很多其他的java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要由以下几种: 1.J2EE容器所提供的JTA实现(JBoss) 2.独立的JTA实现:如JOTM,Atomikos.
这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。
JTS规范
事务是编程中必不可少的一项内容,基于此,为了规范事务开发,Java增加了关于事务的规范,即JTA和JTS
JTA定义了一套接口,其中约定了几种主要的角色:TransactionManager、UserTransaction、Transaction、XAResource,并定义了这些角色之间需要遵守的规范,如Transaction的委托给TransactionManager等。
JTS也是一组规范,上面提到JTA中需要角色之间的交互,那应该如何交互?JTS就是约定了交互细节的规范。
总体上来说JTA更多的是从框架的角度来约定程序角色的接口,而JTS则是从具体实现的角度来约定程序角色之间的接口,两者各司其职。
成熟的中间件
Atomikos公司旗下有两款著名的分布事务产品:
TransactionEssentials:开源的免费产品
ExtremeTransactions:商业版,需要收费
atomikos也支持与spring事务整合。
spring事务管理器的顶级抽象是PlatformTransactionManager接口,其提供了个重要的实现类:
DataSourceTransactionManager:用于实现本地事务
JTATransactionManager:用于实现分布式事务
显然,在这里,我们需要配置的是JTATransactionManager。
XA的主要限制
必须要拿到所有数据源,而且数据源还要支持XA协议。目前MySQL中只有InnoDB存储引擎支持XA协议。
性能比较差,要把所有涉及到的数据都要锁定,是强一致性的,会产生长事务。
Seata AT 模式
Seata AT 模式是增强型2pc模式。
AT 模式: 两阶段提交协议的演变,没有一直锁表
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
二阶段:提交异步化,非常快速地完成。或回滚通过一阶段的回滚日志进行反向补偿
LCN(2pc)
TX-LCN , 官方文档 , github , 3千多星 , 5.0以后由于框架兼容了LCN(2pc)、TCC、TXC 三种事务模式,为了区分LCN模式,特此将LCN分布式事务改名为TX-LCN分布式事务框架。
TX-LCN定位于一款事务协调性框架,框架其本身并不生产事务,而是本地事务的协调者,从而达到事务一致性的效果。
TX-LCN 主要有两个模块,Tx-Client(TC) ,Tx-Manager™.
TM (Tx-Manager):是独立的服务,是分布式事务的控制方,协调分布式事务的提交,回滚
TC(Tx-Client):由业务系统集成,事务发起方、参与方都由TxClient端来控制
2PC(标准XA模型)
2PC即Two-Phase Commit,二阶段提交。为了使得基于分布式架构的所有节点可以在进行事务处理时能够保持原子性和一致性。绝大部分关系型数据库,都是基于2PC完成分布式的事务处理。顾名思义,2PC分为两个阶段处理,阶段一:提交事务请求、阶段二:执行事务提交;如果阶段一超时或者出现异常,2PC的阶段二:中断事务
阶段一:提交事务请求
事务询问。协调者向所有参与者发送事务内容,询问是否可以执行提交操作,并开始等待各参与者进行响应;
执行事务。各参与者节点,执行事务操作,并将Undo和Redo操作计入本机事务日志;
各参与者向协调者反馈事务问询的响应。成功执行返回Yes,否则返回No。
阶段二:执行事务提交
协调者在阶段二决定是否最终执行事务提交操作。这一阶段包含两种情形:
执行事务提交 所有参与者reply Yes,那么执行事务提交。
发送提交请求。协调者向所有参与者发送Commit请求;
事务提交。参与者收到Commit请求后,会正式执行事务提交操作,并在完成提交操作之后,释放在整个事务执行期间占用的资源;
反馈事务提交结果。参与者在完成事务提交后,写协调者发送Ack消息确认;
完成事务。协调者在收到所有参与者的Ack后,完成事务。
阶段二:中断事务
事情总会出现意外,当存在某一参与者向协调者发送No响应,或者等待超时。协调者只要无法收到所有参与者的Yes响应,就会中断事务。
发送回滚请求。协调者向所有参与者发送Rollback请求;
回滚。参与者收到请求后,利用本机Undo信息,执行Rollback操作。并在回滚结束后释放该事务所占用的系统资源;
反馈回滚结果。参与者在完成回滚操作后,向协调者发送Ack消息;
中断事务。协调者收到所有参与者的回滚Ack消息后,完成事务中断。
2pc解决的是分布式数据强一致性问题
顾名思义,两阶段提交在处理分布式事务时分为两个阶段:voting(投票阶段,有的地方会叫做prepare阶段)和commit阶段。
2pc中存在两个角色,事务协调者(seata、atomikos、lcn)和事务参与者,事务参与者通常是指应用的数据库。
2PC二阶段提交的特点
2PC方案比较适合单体应用
2PC 方案中,有一个事务管理器的角色,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok,那么就正式提交事务,在各个数据库上执行操作;如果任何其中一个数据库回答不 ok,那么就回滚事务。
2PC 方案比较适合单体应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。
2PC 方案实际很少用,一般来说某个系统内部如果出现跨多个库的这么一个操作,是不合规的。我可以给大家介绍一下, 现在微服务,一个大的系统分成几百个服务,几十个服务。一般来说,我们的规定和规范,是要求每个服务只能操作自己对应的一个数据库。
如果你要操作别的服务对应的库,不允许直连别的服务的库,违反微服务架构的规范,你随便交叉胡乱访问,几百个服务的话,全体乱套,这样的一套服务是没法管理的,没法治理的,可能会出现数据被别人改错,自己的库被别人写挂等情况。
如果你要操作别人的服务的库,你必须是通过调用别的服务的接口来实现,绝对不允许交叉访问别人的数据库。
2PC具有明显的优缺点:
优点主要体现在实现原理简单; 缺点比较多:
2PC的提交在执行过程中,所有参与事务操作的逻辑都处于阻塞状态,也就是说,各个参与者都在等待其他参与者响应,无法进行其他操作;
协调者是个单点,一旦出现问题,其他参与者将无法释放事务资源,也无法完成事务操作;
数据不一致。当执行事务提交过程中,如果协调者向所有参与者发送Commit请求后,发生局部网络异常或者协调者在尚未发送完Commit请求,即出现崩溃,最终导致只有部分参与者收到、执行请求。于是整个系统将会出现数据不一致的情形;
保守。2PC没有完善的容错机制,当参与者出现故障时,协调者无法快速得知这一失败,只能严格依赖超时设置来决定是否进一步的执行提交还是中断事务。
总结一下: XA-两阶段提交协议中会遇到的一些问题
性能问题
从流程上我们可以看得出,其最大缺点就在于它的执行过程中间,节点都处于阻塞状态。各个操作数据库的节点此时都占用着数据库资源,只有当所有节点准备完毕,事务协调者才会通知进行全局提交,参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。
协调者单点故障问题
事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,会导致参与者收不到提交或回滚的通知,从而导致参与者节点始终处于事务无法完成的中间状态。
丢失消息导致的数据不一致问题
在第二个阶段,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就会导致节点间数据的不一致问题。
3PC
作为2PC的改进版,3PC将原有的两阶段过程,重新划分为CanCommit、PreCommit和do Commit三个阶段。
阶段一:CanCommit
事务询问。协调者向所有参与者发送包含事务内容的canCommit的请求,询问是否可以执行事务提交,并等待应答;
各参与者反馈事务询问。正常情况下,如果参与者认为可以顺利执行事务,则返回Yes,否则返回No。
阶段二:PreCommit
在本阶段,协调者会根据上一阶段的反馈情况来决定是否可以执行事务的PreCommit操作。有以下两种可能:
执行事务预提交
发送预提交请求。协调者向所有节点发出PreCommit请求,并进入prepared阶段;
事务预提交。参与者收到PreCommit请求后,会执行事务操作,并将Undo和Redo日志写入本机事务日志;
各参与者成功执行事务操作,同时将反馈以Ack响应形式发送给协调者,同事等待最终的Commit或Abort指令。
中断事务 加入任意一个参与者向协调者发送No响应,或者等待超时,协调者在没有得到所有参与者响应时,即可以中断事务:
发送中断请求。 协调者向所有参与者发送Abort请求;
中断事务。无论是收到协调者的Abort请求,还是等待协调者请求过程中出现超时,参与者都会中断事务;
阶段三:doCommit
在这个阶段,会真正的进行事务提交,同样存在两种可能。
执行提交
发送提交请求。假如协调者收到了所有参与者的Ack响应,那么将从预提交转换到提交状态,并向所有参与者,发送doCommit请求;
事务提交。参与者收到doCommit请求后,会正式执行事务提交操作,并在完成提交操作后释放占用资源;
反馈事务提交结果。参与者将在完成事务提交后,向协调者发送Ack消息;
完成事务。协调者接收到所有参与者的Ack消息后,完成事务。
中断事务 在该阶段,假设正常状态的协调者接收到任一个参与者发送的No响应,或在超时时间内,仍旧没收到反馈消息,就会中断事务:
发送中断请求。协调者向所有的参与者发送abort请求;
事务回滚。参与者收到abort请求后,会利用阶段二中的Undo消息执行事务回滚,并在完成回滚后释放占用资源;
反馈事务回滚结果。参与者在完成回滚后向协调者发送Ack消息;
中端事务。协调者接收到所有参与者反馈的Ack消息后,完成事务中断。
2PC和3PC的区别:
三阶段提交协议在协调者和参与者中都引入 超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。三阶段提交的三个阶段分别为:can_commit,pre_commit,do_commit。
在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者abort请求时,会在等待超时之后,继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时, 由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 )
3PC主要解决的单点故障问题:
相对于2PC,3PC主要解决的单点故障问题,并减少阻塞, 因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。
但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。
"3PC相对于2PC而言到底优化了什么地方呢?"
相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。这解决了一个什么问题呢?
这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。
另外,通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的。
以上就是3PC相对于2PC的一个提高(相对缓解了2PC中的前两个问题),但是3PC依然没有完全解决数据不一致的问题。假如在 DoCommit 过程,参与者A无法接收协调者的通信,那么参与者A会自动提交,但是提交失败了,其他参与者成功了,此时数据就会不一致。
评论区