什么是分布式事务?此时我我们需要了解一下什么是本地事务;说到本地事务此时我们就需要谈一下什么是事务以及以下几种概念。
事务:
百度百科是这样说的事务(Transaction)
一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
说的过于复杂;简单来说就是,你去超市购买东西,就是一个事务(交钱,交货)。
事务的体现:交钱和交货必须全部成功,事务才能算成功,任何一个活动失败,整个事务就失败了。
说到事务 它的特性有哪些呢?
其实你只要记住四个字母就足够了ACID.
A 原子性(atomicity):事务中的操作要么不做要么全做;要么失败;要么成功.
C 一致性(Consistency):事务的执行结果必须是从数据库中一个一致性状态转换到另一个一致性状态
I 隔离性(Isolation):一个事务的执行不能被另一个事务干扰
D 持久性(Durability):一旦一个事务提交 它的数据库状态的改变是永久性的
上面说完了关于事务的特性 接下来简单说一下事物的隔离级别,一带而过不然你也不能彻底的理解事务
Read Uncommitted:读未提交
Read Committed:读已提交
Repeatable Rea d:可重复读
Serializable:序列化
这里简单说一下具体网上有很多例子 就不详细说明了
本地事务:
通俗来讲就是基于关系型数据库的事务被称为本地事务。
分布式事务:
简单来讲就是分布式系统环境下有不同服务通过网络远程协作完成的事务称之为分布式事务;跨JVM进程产生的分布式事务。例如用户注册送积分事务创建订单减库存事务、银行转账事务
说到分布式事务这里不得不提CAP原则、又称CAP定理。
C (Consistency):一致性
A (Availability):可用性
P (Partition tolerance):分区容错性
C、A很简单这里就不说了,什么是分区容错性呢:
分区容错性指的就是系统部分节点出现故障后,连接正常节点还可以使用系统提供的服务
借鉴一下知乎老表说的话:
一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。
当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。
提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。
然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。
总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。
链接:https://www.zhihu.com/question/54105974/answer/139037688
通过以上CAP结论分布式系统只能满足CAP中的其中两项。由此推出了BASE理论。
BASE理论:
BA Basically Available (基本可用的)
S Soft state (软状态)
E Eventually consistent (最终一致性)
BASE通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能的使用,允许数据在一段时间内是不一致的,但最终达到一致性。满足BASE理论的事务,我们称之为”柔性事务”
柔性事务:
基本可用、软状态、最终一致性。
了解了以上的相关的事务理论知识;下面来谈一下分布式事务的解决方案:针对不同的场景有不同的解决方案例如 2PC、TCC、可靠消息最终一致性、最大努力通知这几种。
分布式事务众多解决方案之2PC(两阶段提交):
两阶段提交协议: 准备阶段(Prepare) 提交阶段(commit phase) 2是指两个阶段准备阶段 和提交阶段
下面展示2PC两个阶段:(RM:资源管理器)


2PC的解决方案:
2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持2PC协议、为了统一标准减少行业内不必要的对接成本,需要指定标准化的处理模型及接口标准,国际开放标准组织Open Group定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)
1、XA方案
XA方案简单举例来讲;
首先我们先来统一下语境和语义.....
DTP模型定义如下角色:
AP(application program):应用程序 可以理解为DTP分布式事务的程序
RM(resource management):资源管理器,可以简单理解为事务的参与者,一般指数据库实例,比如通过资源管理器对数据库进行控制,资源管理器控制着分支事务
TM(transaction management):事务管理器,负责协调管理事务 控制着全局事务,管理事务周期,并协调各个RM。全局事务是指分布式事务中的处理环境中,需要操作多个数据库完成一个工作,这个工作即是一个全局事务。
以上三种角色的交互方式如下:
l TM向AM提供应用程序编程接口,AM通过TM提交回滚事务
l TM交易中间件通过XA接口通知RM数据库事务的开始、回滚、提交
结束等。
总结:
整个2PC的事务流程涉及3个角色,AP、RM、TM、
AP指的是2PC分布式事务的应用程序
RM指的是资源管理器,它控制着分支事务
TM是事务管理器,它控制着全局事务
为了大家能更好大去理解,利用新用户注册送优惠券举例说明.......

执行流程如下:
1. AP(应用程序)持有用户库和券库两个资源
2. AP通过TM通知用户库RM新增用户,同时通知券库RM为该新增用户新增优惠券,RM此时并未提交事务,此时用户资源和优惠券资源暂时锁定
3. TM收到执行的结果回复,只要有一方失败则分别向其他RM发起回滚事务,回滚完毕资源释放
4. TM收到执行的结果回复,全部成功 此时向所有的RM发起提交事务,提交完毕资源释放。
以上就是XA方案的大致流程虽然解决了问题但是也出现了新的情况。
在准备阶段RM执行实际的业务操作,但不提交事务,资源锁定;
在提交阶段TM会接受RM在准备阶段的执行回复,只要有任一个RM执行失败,TM会通知所有RM执行回滚操 作,否则,TM将会通知所有RM提交该事务。提交阶段结束资源锁释放。
XA方案的问题:
1、需要本地数据库支持XA协议。
2、资源锁需要等到两个阶段结束才释放,性能较差。
以上就是本期介绍的分布式事务的XA解决方案。。
什么是TCC事务
TCC是Try、Confirm、Cancel三个词语的缩写,TCC需要每个事务实现三个操作:预处理Try 确认Confirm 取消Cancel。Try操作做业务检查和资源预留,Confirm做业务确认操作,Cancel实现与Try相反的操作即回滚操作。TM首先发起所有分支事务的Try操作,任何一个分支事务Try操作执行失败,TM将会发起所有分支事务的Cancel操作,若Try操作全部成功,TM将会发起所有事务的Confirm操作,其中Confirm/Cancel操作执行失败,TM都会重试。

分布式事务失败情况:

TCC分为3个阶段:
1. Try阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是初步操作,它和后续的Confirm操作一起才能真正构成一个完整的业务逻辑。
2. Confirm阶段是确认提交的,Try阶段所有分支事务执行成功后开始执行Confirm。通常情况下,采用TCC则认为Confirm是不会失败出错的。即:只要Try成功,Confirm就一定会成功。若Confirm阶段出错了,就需要引入重试机制或者人工处理。
3. Cancel阶段是业务执行错误需要回滚的状态下执行分布式事务的业务取消。预留资源释放,通常情况下 采用TCC则认为Cancel是一定成功的。若Cancel阶段真的出错了,也需要Confirm一样进行处理
4. TM事务管理器可以实现独立的服务,也可以让全局事务发起方担任TM的角色,TM独立出来是为了成为独立的组件,是为了考虑结构和软件的复用。
TM发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用记录事务上下文,追踪和记录状态,由于Confirm和Cancel失败需要重试,因此需要实现幂等,幂等指的是同一个操作无论请求多少次,其结果都相同。
TCC需要注意的三种异常处理_分别是空回滚、幂等、悬挂空回滚:在没有调用TCC资源Try方法的情况下,调用了二阶段的Cancel
方法。Cancel方法需要识别出这是一个空回滚,然后直接返回成功。
出现原因:当一个分支事务所在服务宕机或网络出现异常,分支事务调用记录失败,这个时候其实是没有执行Try的,当故障恢复后,分布式事务进行回滚则会调用二阶段的Cancel方法,从而形成空回滚
解决思路:关键是要识别出这个空回滚。思路很简单就是需要知道一阶段是否执行,如果执行了,那就是正常回滚;如果没执行,那就是空回滚。前面已经说过TM发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条。再额外增加一张分布式事务记录表,其中有全局事务ID和分支事务ID,第一阶段Try方法里会插入一条记录,表示阶段一执行了。Cancel接口里读取该记录,如果该记录存在,则正常回滚,否则则是空回滚。
幂等:通过前面介绍了解到,为了保证TCC二阶段提交重试机制不会引发数据不一致,则要求TCC的二阶段Try、Confirm、和Cancel接口保证幂等,这样不会重复使用或者释放资源。如果幂等性没有控制好,则很有可能导致数据不一致等严重问题。
解决思路:在上述分布式事务记录中增加执行状态,每次执行前都查询该状态。
悬挂:悬挂就是对于一个分布式事务,其二阶段Cancel接口比Try接口先执行。
出现原因:可能是在RPC调用分布式事务try时,先注册分支事务,再执行RPC调用,如果此时RPC调用的网络发生拥堵,通常RPC调用是有超时时间的,RPC超时以后,TM就会通知RM回滚该分支事务,可能回滚完成后,RPC请求才到达参与者真正执行,而一个Try方法预留的业务资源。只有该分布式事务才能使用,该分布式事务的一阶段预留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务预留资源后没法急需处理。
解决思路:如果是二级段执行完成,那一阶段就不能再继续执行。在执行一阶段事务时判断在该全局事务下,分支事务记录表中是否已经有二阶段事务记录,,如果有则不执行Try.
什么是可靠消息最终一致性事务?
可靠消息最终一致性方案指的是当事务发起方执行完本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。
此方案利用消息中间件完成,如下图:
事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,事务发起方和消息中间件之间,事务参与方(消息消费方)和消息中间件之间都是通过网络通信的,由于网络通信的不确定性会导致分布式事务有问题。

因此可靠消息最终一致性需要解决以下几个问题:
1. 本地事务与消息发送的原子性问题
(1) 本地事务与消息发送的原子性问题即:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。即实现本地事务和消息发送的原子性,要么都成功 要么都失败。本地事务与消息发送的原子性是实现可靠消息最终一致性方案的关键问题。
但是还是会有问题:假设你先发送消息,再去操作数据库:
这种情况下无法保证数据库与发送消息的一致性,因为消息有可能发送成功,数据库操作失败。
假设你先操作数据库,再发送消息:
这样看起来可以规避一些问题,如果发送MQ消息失败,抛出异常,导致数据库回滚。但是如果是超时异常,数据库回滚,其实此时MQ已经正常发送了,同样会导致不一致的问题。
2. 事务参与方接收消息的可靠性
事务参与方必须能够从消息队列中接收到消息,如果接收消息失败可以重新接收消息。
3. 消息消费重复的问题
由于网络2的存在,若某一个消息节点超时但是消息消费成功,此时消息中间件就会重复投递此消息,从而导致消息的重复消费。
要解决消息重复消费的问题就要实现事务参与方的方法幂等性。
针对以上问题的解决方案:
本地消息解决方案:
本地消息表这个方案最初是eBay提出的,此方案的核心是通过本地事务保证数据业务和消息的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消息方成功再将消息删除。
RocketMQ事务消息解决方案:(此模块后续详细更细)
什么是最大努力通知
最大努力通知也是解决分布式事务的方案,发起通知方通过一定的机制最大努力将业务处理结果通知到接收方。
1. 有一定的消息重复通知机制
因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。
2. 消息校对机制
(1) 如果最大努力也没有通知到接收方,或者接收方消费消息后需要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求
最大努力通知与可靠消息一致性有什么不同?
可靠消息一致性,发起通知方需要保证消息发出去,并且将消息发到接收通知方,消息的可靠性由发起者来保证。
最大努力通知,发起通知方尽最大的努力将业务处理结果通知接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的查询接口获取业务处理结果,通知的可靠性关键在接收通知方。
两者的业务应用场景不同
可靠消息一致性关注交易过程的事务一致,以异步的方式完成交易。
最大努力通知关注的是交易后的通知事务,即 将交易结果可靠的通知出去。
技术解决方向不同
可靠消息一致性要解决消息从发出到接收的一致性,即消息发出并且被接收到。
最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)