分布式事务
一、什么是分布式事务
事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销
本地事务:本地事物其实可以认为是数据库提供的事务机
分布式事务:本质上来说,分布式事务就是为了保证不同数据库的数据一致性
简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
二、产生的原因
业务发展,数据库的拆分-分库分表
SOA和微服务架构的使用
多个微服务之间调用异常
- 网络异常、请求超时、数据库异常、程序宕机等
三、构成
- X/OpenDTP 事务模型:是X/Open 这个组织定义的一套分布式事务的标准,也就是定义了规范和 API 接口,由各个厂商进行具体的实现
DTP 是分布式事物处理(Distributed Transaction Processing)的简称
- XA协议:XA是由X/Open组织提出的分布式事务规范。XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口
主流的数据库产品都实现了XA接口,是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁
JTA:Java Transaction API,java根据XA规范提供的事务处理标准
AP:application, 应用程序也就是业务层,微服务等
RM:Resource Manager,资源管理器。一般是数据库,也可以是其他资源管理器,比如消息队列,文件系统
TM:Transaction Manager ,事务管理器、事务协调者,负责接收来自用户程序(AP)发起的 XA 事务指令,并调度和协调参与事务的所有 RM(数据库),确保事务正确完成
事务模型
在分布式系统中,每一个机器节点 能够明确知道自己在进行事务操作过程中的 结果是成功还是失败,但无法直接获取到其他分布式节点的操作结果
当一个事务操作跨越多个分布式节点的时候,为了保持事务处理的 ACID 特性,需要引入一个“协调者”(TM)来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为 AP。 TM 负责调度 AP 的行为,并最终决定这些 AP 是否要把事务真正进行提交到(RM)
四、解决方案
XA协议:
XA协议简单,数据库支持XA协议,开发使用成本比较低
对业务侵⼊很小,最⼤的优势就是对使⽤⽅透明
用户可以像使⽤本地事务⼀样使⽤基于 XA 协议的分布式事务,能够严格保障事务 ACID 特性
虽然mysql在5.5以后支持了XA协议,但是XA协议占用锁资源比较多,高并发常见下无法满足。是一种刚性事务
XA协议使用比较少,性能比较差
TCC柔性事务
| 含义 | 操作方法 |
| ------------------------------------------------------------ | -------- |
| 预留业务资源/数据效验 | Try |
| 确认执行业务操作,提交数据,不做任何业务检查,try成功,confirm必定成功,需保证幂等 | Confirm |
| 取消执行业务操作,回滚数据,需保证幂等,也是常说的补偿性事务 | Cancel |
五、XA协议-两阶段提交2PC
阶段一--准备提交阶段
协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。
参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
阶段二--提交阶段
协调者节点向所有参与者节点发出”正式提交(commit)”的请求。
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”完成”消息。
协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。
异常状态:如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时
协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”回滚完成”消息。
协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。

缺点:
同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
六、XA协议-三阶段提交3PC
由于二阶段提交存在着诸如同步阻塞、单点问题、脑裂等缺陷,所以,研究者们在二阶段提交的基础上做了改进,提出了三阶段提交。主要改动如下:
引入超时机制。同时在协调者和参与者中都引入超时机制。
在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
阶段一--CanCommit可以提交阶段
事务询问:协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。
响应反馈:参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No
阶段二-PreCommit预提交阶段
发送预提交请求:协调者向参与者发送PreCommit请求,并进入Prepared阶段。
事务预提交:参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。
响应反馈:如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
阶段三--doCommit提交阶段
发送提交请求:协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
事务提交:参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
响应反馈:事务提交完之后,向协调者发送Ack响应。
完成事务:协调者接收到所有参与者的ack响应之后,完成事务。
中断请求:第一第二阶段如果任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断
如果提交阶段,协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务
发送中断请求:协调者向所有参与者发送abort请求
事务回滚:参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
反馈结果:参与者完成事务回滚之后,向协调者发送ACK消息
中断事务: 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。

缺点:
在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时, 说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit, 意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 )
七、TCC柔性事务
将事务提交分为
Try:完成所有业务检查( 一致性 ) ,预留必须业务资源( 准隔离性 )
Confirm :对业务系统做确认提交,默认 Confirm阶段不会出错的 即只要Try成功,Confirm一定成功
Cancel : 业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放, 进行补偿性
TCC 事务和 2PC 的类似,Try为第一阶段,Confirm - Cancel为第二阶段,它对事务的提交/回滚是通过执行一段 confirm/cancel 业务逻辑来实现, 并且也并没有全局事务来把控整个事务逻辑

优点:
它把事务运行过程分成 Try、Confirm/Cancel 两个阶段
每个阶段由业务代码控制,这样事务的锁力度可以完全自由控制
不存在资源阻塞的问题,每个方法都直接进行事务的提交
缺点:
在业务层编写代码实现的两阶段提交,原本一个方法,现在却需要三个方法来支持
对业务的侵入性很强,不能很好的复用
注意:
使用TCC时要注意Try - Confirm - Cancel 3个操作的幂等控制,由于网络原因或者重试操作都有可能导致这几个操作的重复执行
八、事务消息
目前mq仅有rocketmq支持分布式事务的事务消息处理,如果其他队列需要事务消息,可以开发个消息服务,自行实现半消息和回查功能
事务消息:消息队列提供类似Open XA的分布式事务功能,通过消息队列事务消息能达到分布式事务的最终一致
半事务消息:暂不能投递的消息,发送方已经成功地将消息发送到了消息队列服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,消息队列服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),该询问过程即消息回查

优点
事务消息不仅可以实现应用之间的解耦,又能保证数据的最终一致性
同时将传统的大事务可以被拆分为小事务,能提升效率
不会因为某一个关联应用的不可用导致整体回滚,从而最大限度保证核心系统的可用性
缺点
不能实时保证数据一致性
极端情况下需要人工补偿,比如 假如生产者成功处理本地业务,消费者始终消费不成功
