分布式事务的介绍和实现方式简介

写在前面的话

跑步确实是有用的,尽管体重变化不明显,但是体型变化就比较明显了。

像她明天会出现那样期待,像她永远不会来那样生活。

事务简介

这里套用在百度百科中的解释:

事务(Transaction),一般是指要做的或所做的事情。

在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。

事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

在关系数据库中,一个事务由一组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。

原子性(atomicity)

一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

一致性(consistency)

事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务的中间状态不能被观察到的。

隔离性(isolation)

一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

隔离性又分为四个级别:、

读未提交(read uncommitted)

读已提交(read committed,解决脏读)

可重复读(repeatable read,解决虚读)

串行化(serializable,解决幻读)

持久性(durability)

持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

任何事务机制在实现时,都应该考虑事务的ACID特性,包括:本地事务、分布式事务,即使不能都很好的满足,也要考虑支持到什么程度。

本地事务

单个服务对应单个数据库,事务也容易控制,事务直接由数据库提空控制。

具备ACID属性,也易于理解。

也可以直接使用Transactional注解开启事务,对应的可以设置事务的传播机制。

和异常回滚。

	/**
     * 创建商品
     */
    @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
    int create(PmsProductParam productParam);

分布式事务的产生原因或者说场景

前提:
在单体架构中,事务的保证有MySQL实现,不用我们实现,

当单个数据库的性能出现瓶颈的时候,对数据库进行分表分库处理,

按业务纬度将订单和库存的表分为两个库,订单库和库存库。

在分布式服务的情况下,不同的服务,操作不同的数据库。

典型应用场景

例子1 跨库事务:

简单的说,一个服务,操作了两个数据库。

一个完整的业务流程是A服务和B服务配合完成的,具体点就是一个买东西的业务,

A服务写入订单,B服务去扣除库存。

然后这两个服务是分别部署在dbA、dbB服务器上的,即不同库。

这是可能就会发生A服务写入订单在dbA成功,但是B服务在dbB中扣除库存失败。

这就造成了分布式事务问题。

例子二:分库分表

业务发展迅速,用户量变多,单数据已经出现了性能瓶颈,按照业务纬度进行分库,

分为订单库和库存库,由于跨库跨机器,MySQL的本地事务不能再保证订单库和库存库的数据一致性,

这时候就需要分布式事务来保证。

对于分库分表的情况,一般开发人员都会使用一些数据库中间件来降低sql操作的复杂性。

如,对于sql:insert into user(id,name) values (1,“tianshouzhi”),

(2,“wangxiaoxiao”)。这条sql是操作单库的语法,单库情况下,可以保证事务的一致性。

但是由于现在进行了分库分表,开发人员希望将1号记录插入分库1,2号记录插入分库2。

所以数据库中间件要将其改写为2条sql,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都失败,

因此基本上所有的数据库中间件都面临着分布式事务的问题。

例子三:分系统,服务化(SOA)即微服务

业务拓展,单体架构已经满足不了需求,进而衍化成了分布式系统,

这时的订单和库存已经拆分为了两个子系统提供服务,子系统间使用RPC进行通信,但是无论系统发展成什么样,

我们都要保证业务不出问题,保证订单和库存的数据一致,

即分布式事务一致性。

微服务架构例子某个应用同时操作了9个库,应用业务逻辑必然非常复杂,

对于开发人员是极大的挑战,应该拆分成不同的独立服务,

以简化业务逻辑。拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。

下图演示了一个3个服务之间彼此调用的架构:

Service A完成某个功能需要直接操作数据库,

同时需要调用Service B和Service C,而Service B又同时操作了2个数据库,

Service C也操作了一个库。

需要保证这些跨服务的对多个数据库的操作要不都成功,要不都失败,

是最典型的分布式事务场景。

再提一下CAP和BASE理论

CAP

Consistency Acailability Partition tolerance的简写

Consistency:一致性,如果对某个客户端来说,读操作能够返回最新的写操作结果。

Acailability:可用性,非故障节点在合理的时间内返回合理的响应。

Partition tolerance:分区容错性,当出现网络分区后,系统能够继续提供服务。

你知道什么是网络分区吗?

因为分布式系统中系统肯定部署在多台机器上,无法保证网络做到100%的可靠,所以网络分区一定存在,即P一定存在;

在出现网络分区后,就出现了可用性和一致性的问题,原则上两者不能共存,

我们必须要在这两者之间进行取舍,因此就有了两种架构:CP架构,AP架构。

CP架构

当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性。
4.png

当没有出网络分区时,系统A与系统B的数据一致,X=1

将系统A的X修改为2,X=2

当出现网络分区后,系统A与系统B之间的数据同步数据失败,系统B的X=1

当客户端请求系统B时,为了保证一致性,此时系统B应拒绝服务请求,返回错误码或错误信息

上面这种方式就违背了可用性的要求,只满足一致性和分区容错,即CP。

CAP理论是忽略网络延迟,从系统A同步数据到系统B的网络延迟是忽略的。

CP架构保证了客户端在获取数据时一定是最近的写操作,或者获取到异常信息,绝不会出现数据不一致的情况。

保证一致性和分区容错性,一定程度上的忽略可用性。

AP架构

当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。

同样的当没有出网络分区时,系统A与系统B的数据一致,X=1

再将系统A的X修改为2,X=2

这时当出现网络分区后,如果系统A与系统B之间的数据同步数据失败,系统B的X=1

当客户端请求系统B时,为了保证可用性,此时系统B应返回旧值,X=1

上面这种方式就违背了一致性的要求,只满足可用性和分区容错,即AP。

CP架构保证了客户端在获取数据时无论返回的是最新值还是旧值,系统一定是可用的。

保证可用性和分区容错性,一定程度上的忽略一致性。
CAP理论关注粒度是数据,而不是整体系统设计的策略。

BASE理论

BASE理论指的是基本可用Basically Available,软状态Soft Stat,

即最终一致性Eventual Consistency,核心思想是即便无法做到强一致性,但应该可以有采用适合的方式保证最终一致性。

BASE:Basically Available Soft Stat Eventual Consistency的简写。

BA:Basically Available基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

S:Soft Stat软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性。

E:Consistency最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。

BASE理论本质上是对CAP理论的延伸,是对CAP中AP方案的一个补充。

这也是分布式事物中用得比较广泛的一种解决思想。

在分布式事务中就需要用到全局事务的一个概念。

X/Open DTP模型与XA规范

X/Open

即现在的open group,是一个独立的组织,主要负责制定各种行业技术标准。其中华为也是一员

DTP模型

构成DTP模型的5个基本元素:

应用程序(Application Program ,简称AP):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作。

资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。

事务管理器(Transaction Manager ,简称TM):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等。

通信资源管理器(Communication Resource Manager,简称CRM):控制一个TM域(TM domain)内或者跨TM域的分布式应用之间的通信。

通信协议(Communication Protocol,简称CP):提供CRM提供的分布式应用节点之间的底层通信服务。

一个DTP模型实例,至少有3个组成部分:AP、RMs、TM。如下所示:

这图类似跨库事务的概念,即单个应用需要操作多个库。

在这里就是一个AP需要操作多个RM上的资源。

AP通过TM来声明一个全局事务,然后操作不同的RM上的资源,最后通知TM来提交或者回滚全局事务。

分布式事物的实现方式

常见的实现方式:XA和TCC。

XA方式

首先说一下XA规范。

XA规范的最主要的作用是,就是定义了RM-TM的交互接口,下图更加清晰了演示了XA规范在DTP模

型中发挥作用的位置,从下图中可以看出来,XA仅仅出现在RM和TM的连线上。

注意点:

XA规范除了定义的RM-TM交互的接口(XA Interface)之外,还对两阶段提交协议进行了优化。

这里不要误认为两阶段提交协议是在XA规范中提出来的。

事实上: 两阶段协议(two-phase commit)是在OSI TP标准中提出的;

在DTP参考模型(Distributed Transaction Processing: Reference Model)中,

指定了全局事务的提交要使用two-phase commit协议;

而XA规范( Distributed Transaction Processing: The XA Specification)只是定义了两阶段提交协议中需要使用到的接口,

也就是上述提到的RM-TM交互的接口,因为两阶段提交过程中的参与方,只有TM和RMs。

又分为2PC和3PC。

2PC(Two-phase Commit)

称为两阶段提交协议。

定义上:为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法。

在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,

却无法知道其他节点的操作的成功或失败。

当一个事务跨越多个节点时,为了保持事务的ACID特性,

需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示

这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。

二阶段提交的算法思路可以概括为:

参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

二阶段提交算法的成立基于以下假设:

1.该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts)。且节点之间可以进行网络通信。

2.所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。

3.所有节点不会永久性损坏,即使损坏后仍然可以恢复。

二阶段就是投票阶段和提交阶段。

投票阶段

在投票阶段
1.协调者会向所有参与者询问是否可以执行提交操作,并开始等待各参与者的响应。

2.参与者执行事务操作,如果执行成功就返回Yes响应,如果执行失败就返回No响应

3.如果协调者接受参与者响应超时,也会认为执行事务操作失败

提交阶段


1.如果第一阶段汇中所有参与者都返回Yes响应,协调者向所有参与者发出提交请求,所有参与者提交事务。

2.如果第一阶段中有一个或者多个参与者返回No响应,协调者向所有参与者发出回滚请求,所有参与者进行回滚操作。

XA规范对两阶段提交协议的2点优化
1.只读断言

在参与者中,参与者可以断言“我这边不涉及数据增删改”来答复协调者的投票(请求)阶段,从而让这个参与者脱离当前的全局事务,从而免去了提交(请求)阶段。

这种优化发生在其他参与者都完成投票(准备)之前的话,使用了只读断言的参与者早于应用程序其他动作(比如说这个参与者返回那些只读数据给应用程序)前,

就释放了相关数据的上下文(比如读锁之类的),

这时候其他全局事务或者本地事务就有机会去改变这些数据,

结果就是无法保障整个系统的可序列化特性——通俗点说那就会有脏读的风险。

2.一阶段提交

如果需要增删改的数据都在同一个RM上,TM可以使用一阶段提交——跳过两阶段提交中的Phase 1,直接执行Phase 2。

这种优化的本质是跳过Phase 1,RM自行决定了事务分支的结果,并且在答复TM前就清除掉事务分支信息。对于这种优化的情况,TM实际上也没有必要去可靠的记录全局事务的信息,在一些异常的场景下,此时TM可能不知道事务分支的执行结果。

优点

尽可能的保证了事务的一致性,但不是100%。

缺点

1.同步阻塞

由于所有节点在执行操作时都是同步阻塞的,当参与者占有公共资源时,

其他第三方节点访问公共资源不得不处于阻塞状态。

两阶段提交方案下全局事务的ACID特性,是依赖于RM的。

如果我们使用mysql来支持XA分布式事务的话,那么最好将事务隔离级别设置为SERIALIZABLE。

SERIALIZABLE(串行化)是四个事务隔离级别中最高的一个级别,也是执行效率最低的一个级别。

2.单点故障
由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞,尤其时在第二阶段,

协调者发生故障,那么所有的参与者都处于锁定事务资源的状态中,而无法继续完成事务操作。

即使重新选举一个协调者,也无法解决因为协调者宕机导致的参与者处于阻塞状态的问题

3.数据不一致

在第二阶段中,当协调者想参与者发送提交事务请求之后,发生了局部网络异常或者在发送

提交事务请求过程中协调者发生了故障,

这会导致只有一部分参与者接收到了提交事务请求。

而在这部分参与者接到提交事务请求之后就会执行提交事务操作。

但是其他部分未接收到提交事务请求的参与者则无法提交事务。

从而导致分布式系统中的数据不一致。

2PC的问题

如果协调者在第二阶段发送提交请求之后挂掉,

而唯一接受到这条消息的参与者执行之后也挂掉了,

即使协调者通过选举协议产生了新的协调者并通知其他参与者进行提交或回滚操作的话,

也可能会与这个已经执行的参与者执行的操作不一样,

当这个挂掉的参与者恢复之后,就会产生数据不一致的问题。

TCC也可以称为2PC的一中实现方式

3PC提交协议(Three-phase commit)

称为三阶段提交,是二阶段提交的升级版。

是在两阶段提交的基础上,为了解决阻塞问题又增加了超时机制,而且增加了一个准备阶段。

三阶段提交是“非阻塞”协议。

三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个准备阶段,

使得原先在两阶段提交中,参与者在投票之后,由于协调者发生崩溃或错误,

而导致参与者处于无法知晓是否提交或者中止的“不确定状态”所产生的可能相当长的延时的问题得以解决。

询问阶段 CanCommit

协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

准备阶段 PreCommit

协调者根据参与者在询问阶段的响应判断是否执行事务还是中断事务:

如果所有参与者都返回Yes,则执行事务

如果参与者有一个或多个参与者返回No或者超时,则中断事务

参与者执行完操作之后返回ACK响应,同时开始等待最终指令

提交阶段 DoCommit

协调者根据参与者在准备阶段的响应判断是否执行事务还是中断事务:

如果所有参与者都返回正确的ACK响应,则提交事务。

如果参与者有一个或多个参与者收到错误的ACK响应或者超时,则中断事务。

如果参与者无法及时接收到来自协调者的提交或者中断事务请求时,

会在等待超时之后,会继续进行事务提交

协调者收到所有参与者的ACK响应,完成事务。

解决二阶段提交时的问题

在三阶段提交中,如果在第三阶段协调者发送提交请求之后挂掉,

并且唯一的接受的参与者执行提交操作之后也挂掉了,

这时协调者通过选举协议产生了新的协调者,

在二阶段提交时存在的问题就是新的协调者不确定已经执行过事务的参与者是执行的提交事务还是中断事务,

但是在三阶段提交时,肯定得到了第二阶段的再次确认,

那么第二阶段必然是已经正确的执行了事务操作,只等待提交事务了,

所以新的协调者可以从第二阶段中分析出应该执行的操作,

进行提交或者中断事务操作,这样即使挂掉的参与者恢复过来,数据也是一致的。

所以,三阶段提交解决了二阶段提交中存在的由于协调者和参与者同时挂掉可能导致的数据一致性问题和单点故障问题,

并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,

他会默认执行提交事务,而不会一直持有事务资源并处于阻塞状态。

三阶段提交的问题

在提交阶段如果发送的是中断事务请求,

但是由于网络问题,导致部分参与者没有接到请求,

那么参与者会在等待超时之后执行提交事务操作,

这样这些由于网络问题导致提交事务的参与者的数据就与接受到中断事务请求的参与者存在数据不一致的问题。

2pc和3PC的区别

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,

他会默认执行commit,而不会一直持有事务资源并处于阻塞状态。

但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,

那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

解决一致性方案

针对XA中2PC和3PC都无法满足100%一致性的问题,提出来强一致性解决方式和柔性事务解决方式。

强一致性分布式事务

前提:

单体架构多数据源,在业务开发中,肯定是先执行对订单库的操作,但是不提交事务,

再执行对库存库的操作,也不提交事务,如果两个操作都成功,

在一起提交事务,如果有一个操作失败,则两个都进行回滚。

基于2PC/XA协议实现的JTA

JTA是Java规范,是XA在Java上的实现。

JTA(Java Transaction Manager):

TransactionManager:常用方法,可以开启,回滚,获取事务。begin(),rollback()……

XAResouce:资源管理,通过Session来进行事务管理,commit(xid)……

XID:每一个事务都分配一个特定的XID

JTA主要的原理是二阶段提交,

当整个业务完成了之后只是第一阶段提交,

在第二阶段提交之前会检查其他所有事务是否已经提交,

如果前面出现了错误或是没有提交,

那么第二阶段就不会提交,而是直接回滚,这样所有的事务都会做回滚操作。

基于JTA这种方案实现分布式事务的强一致性。

JTA的特点

1.基于两阶段提交,有可能会出现数据不一致的情况

2.事务时间过长,阻塞

3.性能低,吞吐量低

实现使用基于JTA实现的jar包Atomikos。

正常架构设计中应该避免出现这种跨库的操作,如果过按业务拆分将数据源进行分库,

我们应该同时将服务也拆分出去才合适,

应遵循一个系统只操作一个数据源(主从没关系),

避免后续可能会出现的多个系统调用一个数据源的情况。

最终一致性分布式事务方案(柔性事务)

上面的JTA方式是针对单服务,跨数据库的解决方案,但是如果遇上微服务就无能为力了,

这种情况下就需要最终一致性分布式事务方案(柔性事务)来处理了,具体方案如下:

本地消息表

本地消息表的核心思想是将分布式事务拆分成本地事务进行处理。

以本文中例子,在订单系统新增一条消息表,将新增订单和新增消息放到一个事务里完成,

然后通过轮询的方式去查询消息表,将消息推送到MQ,库存系统去消费MQ

执行流程:

1.订单系统,添加一条订单和一条消息,在一个事务里提交

2.订单系统,使用定时任务轮询查询状态为未同步的消息表,发送到MQ,如果发送失败,就重试发送

3.库存系统,接收MQ消息,修改库存表,需要保证幂等操作

4.如果修改成功,调用rpc接口修改订单系统消息表的状态为已完成或者直接删除这条消息

5.如果修改失败,可以不做处理,等待重试

问题点:

订单系统中的消息有可能由于业务问题会一直重复发送,

所以为了避免这种情况可以记录一下发送次数,当达到次数限制之后报警,人工接入处理;

库存系统需要保证幂等,避免同一条消息被多次消费造成数据一致。

本地消息表这种方案实现了最终一致性,需要在业务系统里增加消息表,

业务逻辑中多一次插入的DB操作,所以性能会有损耗,

而且最终一致性的间隔主要由定时任务的间隔时间决定

MQ消息事务

MQ消息事务和本地事务类似,但是又不一样,虽然都借助MQ实现,但是其实不一样。

消息事务的原理是将两个事务通过消息中间件进行异步解耦。

订单系统执行自己的本地事务,并发送MQ消息,
库存系统接收消息,
执行自己的本地事务
,只是省去了对本地消息表的操作和轮询发送MQ的操作,

和本地消息表相比,消息事务一定要保证业务操作与消息发送的一致性,

如果业务操作成功,这条消息也一定投递成功。

消息事务依赖于消息中间件的事务消息,基于消息中间件的二阶段提交实现的,RocketMQ就支持事务消息。

执行流程:

1.发送prepare(准备)消息到消息中间件

2.发送成功后,执行本地事务

3.如果事务执行成功,则commit,消息中间件将消息下发至消费端

4.如果事务执行失败,则回滚,消息中间件将这条prepare(准备)消息删除

5.消费端接收到消息进行消费,如果消费失败,则不断重试

这种方案也是实现了最终一致性,对比本地消息表实现方案,不需要再建消息表,

不再依赖本地数据库事务了,所以这种方案更适用于高并发的场景。

最大努力通知

最大努力通知相比前两种方案实现简单,适用于一些最终一致性要求较低的业务,比如支付通知,短信通知这种业务

以支付通知为例,业务系统调用支付平台进行支付,支付平台进行支付,进行操作支付之后支付平台会尽量去通知业务系统支付操作是否成功,但是会有一个最大通知次数,如果超过这个次数后还是通知失败,就不再通知,业务系统自行调用支付平台提供一个查询接口,供业务系统进行查询支付操作是否成功。

执行流程:
业务系统调用支付平台支付接口,并在本地进行记录,支付状态为支付中
支付平台进行支付操作之后,无论成功还是失败,都需要给业务系统一个结果通知
如果通知一直失败则根据重试规则进行重试,达到最大通知次数后,不在通知
支付平台提供查询订单支付操作结果接口
业务系统根据一定业务规则去支付平台查询支付结果

这种方案也是实现了最终一致性。

补偿事务TCC

TCC Try-Confirm-Cancel的简称,针对每个操作,都需要有一个其对应的确认和取消操作,

当操作成功时调用确认操作,当操作失败时调用取消操作,

类似于二阶段提交,只不过是这里的提交和回滚是针对业务上的,

所以基于TCC实现的分布式事务也可以看做是对业务的一种补偿机制。

TCC的三阶段

Try阶段:对业务系统做检测及资源预留

Confirm阶段:对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功

Cancel阶段:在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放

在Try阶段,是对业务系统进行检查及资源预览,

比如订单和存储操作,需要检查库存剩余数量是否够用,

并进行预留,预留操作的话就是新建一个可用库存数量字段,

Try阶段操作是对这个可用库存数量进行操作。

比如下一个订单减一个库存:

执行流程:

Try阶段:订单系统将当前订单状态设置为支付中,

库存系统校验当前剩余库存数量是否大于1,然后将可用库存数量设置为库存剩余数量-1,

如果Try阶段执行成功,执行Confirm阶段,将订单状态修改为支付成功,库存剩余数量修改为可用库存数量

如果Try阶段执行失败,执行Cancel阶段,将订单状态修改为支付失败,可用库存数量修改为库存剩余数量。

基于TCC实现分布式事务,代码逻辑想对复杂一些,需要将原来的接口的逻辑拆分为:try,confirm,cancel三个接口的逻辑。

基于TCC实现的分布式事务框架有

ByteTCC:github.com/liuyangming

tcc-transaction:github.com/changmingxi

在实际生产中我们要尽量避免使用分布式事务,能转化为本地事务就用本地事务,

如果必须使用分布式事务,还需要从业务角度多思考使用哪种方案更适合。

XA和TCC实现方式的比较

  1. 在阶段1:
    在XA中,各个RM准备提交各自的事务分支,事实上就是准备提交资源的更新操作(insert、delete、update等);而在TCC中,是主业务活动请求(try)各个从业务服务预留资源。

  2. 在阶段2:
    XA根据第一阶段每个RM是否都prepare成功,判断是要提交还是回滚。如果都prepare成功,那么就commit每个事务分支,反之则rollback每个事务分支。

TCC中,如果在第一阶段所有业务资源都预留成功,那么confirm各个从业务服务,否则取消(cancel)所有从业务服务的资源预留请求。

TCC两阶段提交与XA两阶段提交的区别是:

XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。

XA事务中的两阶段提交内部过程是对开发者屏蔽的,根据JTA规范可以了解,

通过UserTransaction的commit方法来提交全局事务,这只是一次方法调用,

其内部会委派给TransactionManager进行真正的两阶段提交,因此开发者从代码层面是感知不到这个过程的。

而事务管理器在两阶段提交过程中,从prepare到commit/rollback过程中,资源实际上一直都是被加锁的。

如果有其他人需要更新这两条记录,那么就必须等待锁释放。

TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。

TCC中的两阶段提交并没有对开发者完全屏蔽,也就是说从代码层面,

开发者是可以感受到两阶段提交的存在。

try、confirm/cancel在执行过程中,一般都会开启各自的本地事务,来保证方法内部业务逻辑的ACID特性。其中:

1、try过程的本地事务,是保证资源预留的业务逻辑的正确性。

2、confirm/cancel执行的本地事务逻辑确认/取消预留资源,以保证最终一致性,也就是所谓的补偿型事务(Compensation-Based Transactions)。

写在后面的话

生命如果不能浪费在我所喜欢的人身上,

那我宁愿把生命浪费在我自己身上,

如果那个人还没有出现,

我会先把自己变成自己喜欢的人。

我不愿去碰触我不喜欢的身体,

回应那些我毫无感觉的词句,

去拥抱那些我从未心动过的灵魂,

亲吻我不爱的人。

我亦不愿将我的所有与之共享,不分彼此。

下集预告

既然讲了强一致性的分布式事务,那么就具体讲讲基于JTA实现的jar包Atomikos实现方式。

参考

终于有人把分布式事务说清楚了

田守枝JAVA技术博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值