🚀 分布式事务 🚀 |
🌲 AI工具、AI绘图、AI专栏 🍀
🌲 如果你想学到最前沿、最火爆的技术,赶快加入吧✨
🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎
🌲 恭喜你发现一枚宝藏博主,赶快收入囊中吧🌻
🌲 人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯
🚀 分布式事务 🚀 |
🍔 目录
- 🌱 一.分布式事务之DTP模型
- 🌱 二.分布式事务之XA协议
- 🌱 三.分布式事务之2PC模式
- 🌱 3.1 2PC协议过程
- 🌱 3.2 2PC缺陷
- 🌱 四.分布式事务之3PC模式
- 🌱 4.1 3PC协议过程
- 🌱 4.2 3PC协议改进的优点
- 🌱 4.3 3PC协议缺陷
- 🌱 五.分布式事务之TCC模式
- 🌱 5.1 TCC协议执行流程
- 🌱 5.2 TCC协议具体案例
- 🌱 5.3 TCC模式的优势和缺点
- 🌱 5.4 TCC使用场景
- 🌱 六.分布式事务之消息队列模式
- 🌱6.1 事务的处理流程
- 🌱6.2 本地消息表
- 🌱6.3 消息事务的优缺点
- 🌱 七.分布式事务之AT模式
- 🌱7.1 AT模式基本原理
- 🌱7.2 AT模式优缺点
- 🌱 八.分布式事务之Saga模式
- 🌱8.1 基本原理
- 🌱8.2 优缺点
- 🌱 九.总结
- 💬 十.共勉
🌱 一.分布式事务之DTP模型
X/Open DTP(Distributed Transaction Process)是一个分布式事务模型。这个模型主要使用了两段提交(2PC - Two-Phase-Commit)来保证分布式事务的完整性。
1994 年,X/Open 组织(即现在的 Open Group )定义了分布式事务处理的DTP 模型。该模型包括这样几个角色:
- 应用程序( AP ):应用是使用DTP的程序,它们通过事务管理器发起和完成分布式事务。
- 事务管理器( TM ):事务管理器是DTP模型的核心组件,它负责协调和管理分布式事务的整个生命周期。事务管理器负责启动、提交、回滚或中止事务,并确保事务的一致性和完整性。
- 资源管理器( RM ):资源管理器是管理数据资源的组件,例如数据库管理系统(DBMS)或消息服务器管理系统。资源管理器负责控制和管理与事务相关的资源,例如数据库连接、消息队列等。
- 通信资源管理器( CRM ):是TM和RM间的通信中间件
在该模型中,一个分布式事务(全局事务)可以被拆分成许多个本地事务,运行在不同的AP和RM上。每个本地事务的ACID很好实现,但是全局事务必须保证其中包含的每一个本地事务都能同时成功,若有一个本地事务失败,则所有其它事务都必须回滚。但问题是,本地事务处理过程中,并不知道其它事务的运行状态。因此,就需要通过CRM来通知各个本地事务,同步事务执行的状态。
因此,各个本地事务的通信必须有统一的标准,否则不同数据库间就无法通信。XA就是 X/Open DTP中通信中间件与TM间联系的接口规范,定义了用于通知事务开始、提交、终止、回滚等接口,各个数据库厂商都必须实现这些接口。
🌱 二.分布式事务之XA协议
XA是由X/Open组织提出的分布式事务的规范,是基于两阶段提交协议。XA 规范主要定义了事务管理器(Transaction Manager)和局部资源管理器(Local Resource Manager)之间的接口。目前主流的关系型数据库产品都是实现了XA接口。
XA之所以需要引入事务管理器,是因为在分布式系统中,从理论上讲两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。由全局事务管理器管理和协调的事务,可以跨越多个资源(数据库)和进程。
事务管理器用来保证所有的事务参与者都完成了第一阶段的准备工作。第二阶段:如果事务管理器收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了。MySQL 在这个XA事务中扮演的是参与者的角色,而不是事务管理器。
🌱 三.分布式事务之2PC模式
🌱 3.1 2PC协议过程
二阶提交协议就是根据这一思想衍生出来的,将全局事务拆分为两个阶段来执行:
- 阶段一:准备阶段,各个本地事务完成本地事务的准备工作。
- 阶段二:执行阶段,各个本地事务根据上一阶段执行结果,进行提交或回滚。
这个过程中需要一个协调者(coordinator),还有事务的参与者(voter)。
第一种情况:正常提交
投票阶段:协调组询问各个事务参与者,是否可以执行事务。每个事务参与者执行事务,写入redo和undo日志,然后反馈事务执行成功的信息(agree
)
提交阶段:协调组发现每个参与者都可以执行事务(agree
),于是向各个事务参与者发出commit
指令,各个事务参与者提交事务。
第二种情况:提交失败
当然,也有异常的时候:
投票阶段:协调组询问各个事务参与者,是否可以执行事务。每个事务参与者执行事务,写入redo和undo日志,然后反馈事务执行结果,但只要有一个参与者返回的是Disagree
,则说明执行失败。
提交阶段:协调组发现有一个或多个参与者返回的是Disagree
,认为执行失败。于是向各个事务参与者发出abort
指令,各个事务参与者回滚事务。
🌱 3.2 2PC缺陷
-
全流程的同步阻塞:不管是第一阶段还是第二阶段,所有参与节点都是事务阻塞型。当参与者占有公共资源时,其他第三方访问公共资源可能不得不处于阻塞状态。
-
TM单点故障:由于全流程依赖TM的协调,一旦TM发生故障。参与者会一直阻塞下去。尤其在第二阶段,TM发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。所有参与者必须等待TM重新上线(TM重新选举)后才能继续工作。
-
TM脑裂引起数据不一致:在第二阶段中,当TM向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中TM发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
-
TM脑裂引起事务状态不确定:TM再发出commit消息之后宕机,而接收到这条消息的参与者同时也宕机了。那么即使通过选举协议产生了新的TM,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
🌱 四.分布式事务之3PC模式
🌱 4.1 3PC协议过程
第一阶段: CanCommit阶段:
TM协调者向所有参与者发送消息,然后各个库返回结果,不会执行实际的SQL语句,就是验证各个库网络的环境,是否准备成功,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”,需要注意的这个阶段存在超时的中止机制。
第二阶段: PreCommit阶段:
- 第一种情况:所有的参与者都回应“可以提交”,协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交,并释放所有资源,然后回应“完成”,协调者收集各个服务的“完成”回应后结束事务。
注意:此时会执行SQL语句,但是依然不会提交,还是会继续确定一遍!
- 第二种情况:如果有一个参与者回应“拒绝提交”协调者向所有的参与者发送“回滚操作”,并释放所有资源,然后回应“回滚完成”,取消整体的分布式事务。
第三阶段: DoCommit阶段
- 第一种情况:PreCommit阶段都返回了成功,那么发送DoCommit消息给各个数据库来完成最后提交事务,各个数据库最后返回提交成功给TM,那么代表分布式事务成功。
- 第二种情况:如果有个别的数据库对PreCommit阶段返回的是失败,那么TM分布式事务失败,直接abort消息给各个数据库,然后执行回滚的操作,各个数据库回滚成功后直接通知TM,分布式事务回滚。
整理流程如下图所示:该图片来自来于互联网
🌱 4.2 3PC协议改进的优点
- 引入CanCommit阶段,用于询问所有参与者是否可以执行事务并且做出响应,可以尽早发现无法执行操作而终止的后续行为。
- DoCommit阶段,事务协调者和参与者都引入了超时的机制,一旦超时,事务协调者和参与者会继续提交事务,并且处于成功的状态,因为在这种情况下,事务默认成功的可能性比较大。
- 资源阻塞问题得到了缓减。
🌱 4.3 3PC协议缺陷
- 如果TM在DoCommit阶段发送了abort消息给各个库,结果因为脑裂的问题,某一个库最后没有收到abort的消息,自己还因为超时的机制执行了commit操作。
🌱 五.分布式事务之TCC模式
TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。
TCC 是服务化的两阶段编程模型,其 Try、Confirm、Cancel 3 个方法均由业务编码实现:
TCC的基本原理
它本质是一种补偿的思路。事务运行过程包括三个方法,
- Try:资源的检测和预留;
- Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
- Cancel:预留资源释放。
🌱 5.1 TCC协议执行流程
执行分两个阶段:
- 准备阶段(try):资源的检测和预留;
- 执行阶段(confirm/cancel):根据上一步结果,判断下面的执行方法。如果上一步中所有事务参与者都成功,则这里执行confirm。反之,执行cancel
TCC与两阶段提交没什么区别,但其实差别很大:
-
try、confirm、cancel都是独立的事务,不受其它参与者的影响,不会阻塞等待它人
-
try、confirm、cancel由程序员在业务层编写,锁粒度有代码控制
🌱 5.2 TCC协议具体案例
以之前的下单业务中的扣减余额为例来看下三个不同的方法要怎么编写,假设账户A原来余额是100,需要余额扣减30元。如图:
-
一阶段(Try):余额检查,并冻结用户部分金额,此阶段执行完毕,事务已经提交
- 检查用户余额是否充足,如果充足,冻结部分余额
- 在账户表中添加冻结金额字段,值为30,余额不变
-
二阶段
- 提交(Confirm):真正的扣款,把冻结金额从余额中扣除,冻结金额清空
- 修改冻结金额为0,修改余额为100-30 = 70元
- 补偿(Cancel):释放之前冻结的金额,并非回滚
- 余额不变,修改账户冻结金额为0
- 提交(Confirm):真正的扣款,把冻结金额从余额中扣除,冻结金额清空
🌱 5.3 TCC模式的优势和缺点
-
优势
TCC执行的每一个阶段都会提交本地事务并释放锁,并不需要等待其它事务的执行结果。而如果其它事务执行失败,最后不是回滚,而是执行补偿操作。这样就避免了资源的长期锁定和阻塞等待,执行效率比较高,属于性能比较好的分布式事务方式。
-
缺点
-
代码侵入:需要人为编写代码实现try、confirm、cancel,代码侵入较多
-
开发成本高:一个业务需要拆分成3个步骤,分别编写业务实现,业务编写比较复杂
-
安全性考虑:cancel动作如果执行失败,资源就无法释放,需要引入重试机制,而重试可能导致重复执行,还要考虑重试时的幂等问题
-
🌱 5.4 TCC使用场景
-
对事务有一定的一致性要求(最终一致)
-
对性能要求较高
-
开发人员具备较高的编码能力和幂等处理经验,try confirm和cancel都有可能被多次调用
🌱 六.分布式事务之消息队列模式
消息队列的方案最初是由 eBay 提出,基于TCC模式,消息中间件可以基于 Kafka、RocketMQ 等消息队列。
此方案的核心是将分布式事务拆分成本地事务进行处理,将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或MQ中间件,再通过业务规则人工发起重试。
🌱6.1 事务的处理流程
-
步骤1:事务主动方处理本地事务。事务主动方在本地事务中处理业务更新操作和MQ写消息操作。例如: A用户给B用户转账,主动方先执行扣款操作
-
步骤 2:事务发起者A通过MQ将需要执行的事务信息发送给事务参与者B。事务主动方主动写消息到MQ,事务消费方接收并处理MQ中的消息。
-
步骤 3:事务被动方通过MQ中间件,通知事务主动方事务已处理的消息,事务主动方根据反馈结果提交或回滚事务。例如: 订单生成成功,通知主动方法,主动放即可以提交.
为了数据的一致性,当流程中遇到错误需要重试,容错处理规则如下:
-
当步骤 1 处理出错,事务回滚,相当于什么都没发生。
-
当步骤 2 处理出错,由于未处理的事务消息还是保存在事务发送方,可以重试或撤销本地业务操作。
-
如果事务被动方消费消息异常,需要不断重试,业务处理逻辑需要保证幂等。
-
如果是事务被动方业务上的处理失败,可以通过MQ通知事务主动方进行补偿或者事务回滚。
🌱6.2 本地消息表
那么问题来了,我们如何保证消息发送一定成功?如何保证消费者一定能收到消息?
为了避免消息发送失败或丢失,我们可以把消息持久化到数据库中。实现时有简化版本和解耦合版本两种方式。
-
事务发起者:
- 开启本地事务
- 执行事务相关业务
- 发送消息到MQ
- 把消息持久化到数据库,标记为已发送
- 提交本地事务
-
事务接收者:
- 接收消息
- 开启本地事务
- 处理事务相关业务
- 修改数据库消息状态为已消费
- 提交本地事务
-
额外的定时任务
- 定时扫描表中超时未消费消息,重新发送
🌱6.3 消息事务的优缺点
总结上面的几种模型,消息事务的优缺点如下:
- 优点:
- 业务相对简单,不需要编写三个阶段业务
- 是多个本地事务的结合,因此资源锁定周期短,性能好
- 缺点:
- 代码侵入
- 依赖于MQ的可靠性
- 消息发起者可以回滚,但是消息参与者无法引起事务回滚
- 事务时效性差,取决于MQ消息发送是否及时,还有消息参与者的执行情况
🌱 七.分布式事务之AT模式
2019年 1 月份,Seata 开源了 AT 模式。AT 模式是一种无侵入的分布式事务解决方案。可以看做是对TCC或者二阶段提交模型的一种优化,解决了TCC模式中的代码侵入、编码复杂等问题。
在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
Seata官方文档。
🌱7.1 AT模式基本原理
有没有感觉跟TCC的执行很像,都是分两个阶段:
- 一阶段:执行本地事务,并返回执行结果
- 二阶段:根据一阶段的结果,判断二阶段做法:提交或回滚
但AT模式底层做的事情可完全不同,而且第二阶段根本不需要我们编写,全部有Seata自己实现了。也就是说:我们写的代码与本地事务时代码一样,无需手动处理分布式事务。
那么,AT模式如何实现无代码侵入,如何帮我们自动实现二阶段代码的呢?
一阶段
-
在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“
业务 SQL
”要更新的业务数据,在业务数据被更新前,将其保存成“before image
”,然后执行“业务 SQL
”更新业务数据,在业务数据更新之后,再将其保存成“after image
”,最后获取全局行锁,提交事务。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。 -
这里的
before image
和after image
类似于数据库的undo和redo日志,但其实是用数据库模拟的。
update t_stock set stock = stock - 2 where id = 1
select * from t_stock where id = 1 ,保存元快照 before image ,类似undo日志.
放行执行真实SQL,执行完成,再次查询,获取到最新的库存数据,再将数据保存到镜像after image 类似redo.
提交业务如果成功,就清楚快照信息,失败,则根据redo 中的数据与数据库的数据进行对比,如果一直就回滚,如果不一致 出现脏数据,就需要人工介入.
AT模式最重要的一点就是 程序员只需要关注业务处理的本身即可,不需要考虑回滚补偿等问题.代码写的跟以前一模一杨.
二阶段提交
- 二阶段如果是提交的话,因为“
业务 SQL
”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段回滚
- 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“
业务 SQL
”,还原业务数据。回滚方式便是用“before image
”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image
”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写
,出现脏写就需要转人工处理。
不过因为有全局锁机制,所以可以降低出现脏写
的概率。
AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。
🌱7.2 AT模式优缺点
优点:
- 与2PC相比:每个分支事务都是独立提交,不互相等待,减少了资源锁定和阻塞时间
- 与TCC相比:二阶段的执行操作全部自动化生成,无代码侵入,开发成本低
缺点:
- 与TCC相比,需要动态生成二阶段的反向补偿操作,执行性能略低于TCC
🌱 八.分布式事务之Saga模式
Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。其理论基础是Hector & Kenneth 在1987年发表的论文Sagas。
Seata官网对于Saga学习知道
🌱8.1 基本原理
在分布式事务场景下,我们把一个Saga分布式事务看做是一个由多个本地事务组成的事务,每个本地事务都有一个与之对应的补偿事务。在Saga事务的执行过程中,如果某一步执行出现异常,Saga事务会被终止,同时会调用对应的补偿事务完成相关的恢复操作,这样保证Saga相关的本地事务要么都是执行成功,要么通过补偿恢复成为事务执行之前的状态。(自动反向补偿机制)。
每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。
Saga是一种补偿模式,它定义了两种补偿策略:
- 向前恢复(forward recovery):对应于上面第一种执行顺序,发生失败进行重试,适用于必须要成功的场景(一定会成功)。
- 向后恢复(backward recovery):对应于上面提到的第二种执行顺序,发生错误后撤销掉之前所有成功的子事务,使得整个 Saga 的执行结果撤销。
🌱8.2 优缺点
优点
- 一阶段提交本地事务,无锁,高性能
- 事件驱动架构,参与者可异步执行,高吞吐
- 补偿服务易于实现
缺点
- 不保证隔离性
🌱 九.总结
本篇文章主要内容分布式事务七种模式理论详解方案,主要有XA协议,2PC,3PC,TCC,消息队列,AT,Saga。更多内容支持创作中,大家敬请期待呦!!
💬 十.共勉
最后,我想和大家分享一句一直激励我的座右铭,希望可以与大家共勉! |