SpringCloud--分布式事务实现

news2024/11/13 3:44:09

一、分布式事务

首先要明白事务是指数据库中的一组操作,这些操作要么全部成功执行,要么全部不执行,以保持数据的一致性和完整性。在本地事务中,也就是传统的单机事务,必须要满足原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)四个特性,通常称为ACID特性。而分布式事务是指跨越多个分布式系统的事务,不再是单机事务,其中涉及到多个独立服务。分布式事务主要是为了保证这多个跨服务的系统之间的操作的一致性和原子性。

二、分布式系统面临的问题

在分布式系统中,通常是根据业务逻辑分成多个微服务独立部署,且每个微服务都有自己单独的数据源。以最常见的用户购买商品的业务逻辑,可以分为3个微服务:

  • 订单服务(Order):用户根据商品的库存量来创建订单。
  • 仓储服务(Stock):创建订单成功后对给定的商品扣除库存量。
  • 账户服务(Account):订单支付成功后从用户账户中扣除余额。

当用户购买一件商品从下单到支付成功,总共要涉及3个服务的操作。由于各个服务间调用可能存在网络延迟、节点故障、通信失败等原因,导致分布式事务无法像单个系统的事务那样简单的就能实现ACID特性。这期间往往会产生许多问题,最常见的如下:

  1. 部分失败:在一个分布式事务中,有些参与者执行成功,而其他参与者执行失败,导致事务的部分操作成功,部分操作失败。
  2. 数据不一致:在一个分布式事务中,数据的一致性无法保证,可能因为参与者之间的数据冲突或者数据同步延迟。

三、分布式理论基础

3.1 CAP定理

CAP定理是分布式当中一个非常重要的理论,指的是在一个分布式系统中一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance):

  • 一致性(Consistency):在任何时刻,对于同一个数据项,所有节点上的值都是相同的。
  • 可用性(Availability):系统在任何时候都能够响应客户端请求,不会出现宕机或不可用的情况。
  • 分区容错性(Partition tolerance):分布式系统中的节点之间可能会出现网络故障,但是无论哪个节点出现故障,整个分布式系统任然能够对外提供服务。

CAP定理中不可能同时满足这三者,最多只能同时满足其中两项:

  • 放弃P(CA):在分布式系统中,系统间的网络不可能100%保证正常,一定会有故障的时候,又想在节点故障的情况下还保证节点间数据的一致性和可用性是不现实的。因此分区容错性不可避免。
  • 放弃A(CP):就是在节点存在故障后,为了保证各个节点间数据的强一致性,就必须等故障节点恢复正常后,再将数据同步过去,在等待故障节点恢复正常的这段时间服务处于阻塞状态,不可用。也就是放弃服务的可用性,从而保证节点间数据的强一致性。
  • 放弃C(AP):就是在节点存在故障后,不用再等故障节点恢复正常,依旧对外提供服务,只不过使用的是故障前的数据提供服务。也就是可能存在节点间的数据不一致,用放弃数据强一致性来实现服务的可用性。其实这里放弃一致性,并不是完全不需要数据一致性,是指放弃数据的强一致性,保留数据的最终一致性。

CAP理论具体应用:

  • Redis :属于 cp 模型。
  • Redis-cluster 属于 ap 模型
  • Zookeeper:属于cp模型。
  • MongoDB :属于cp模型。
  • Eureka:属于ap模型。
3.2 BASE理论

CAPBASE理论指的是:基本可用(Basically Available),软状态(Soft State),最终一致性(Eventual Consistency),核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性。

  • BA:Basically Available 基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
  • S:Soft State 软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
  • E:Eventual Consistency 最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。

一致性可分为以下3类:

  • 强一致性:在任意时刻,所有节点看到的数据都必须是一样的。
  • 弱一致性:数据的更新可能会出现延迟,允许在延迟的这段时间内,节点看到的数据不是最新的。
  • 最终一致性:不保证在任意时刻任意节点上的同一份数据都是相同的,但是在一段时间后,节点间的数据会最终达到一致状态。

四、Seata框架

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
在这里插入图片描述

4.1 Seata中三个重要的角色
  1. TC (Transaction Coordinator) 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
  2. TM (Transaction Manager) 事务管理器:定义全局事务的范围、向TC申请开始全局事务、向TC申请提交或回滚全局事务。
  3. RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
4.2 Seata事务整体执行流程
  1. 由TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;
  2. XID会在微服务调用链路的上下文中传播;
  3. RM向TC注册分支事务,纳入XID对应全局事务的范围;
  4. RM驱动分支事务的执行,并报告分支事务的执行结果给TC;
  5. TM向TC发起针对XID的全局事务提交或回滚决议;
  6. TC根据RM报告的实际分支事务的执行结果进行决策,TC会向RM发送一个提交或回滚消息。
  7. RM会将这个提交或回滚消息广播给所有参与该全局事务的分支服务,让它们各自提交或回滚分支事务。

五、Seata中四种事务模式

5.1 AT模式

AT模式是Seata的默认模式,在该模式下Seata工作在应用层,无业务侵入,主要是通过对本地关系数据库的分支事务的协调来完成全局事务。

5.1.1 前提
  • 基于支持本地 ACID 事务的关系型数据库。
  • Java 应用,通过 JDBC 访问数据库。
5.1.2 整体机制

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:
    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。
5.1.3 写隔离

Seata通过 全局锁 来保证多个全局事务之间的写隔离,从而防止脏写的发生。全局锁 本身就是一条记录,由xid-事务、table-表名、pk-数据的行组成,全局锁 由事务协调者TC控制,只有持有该全局锁的全局事务,才具备本地事务的执行权。

  • 一阶段本地事务提交前,需要确保先拿到 全局锁
  • 拿不到 全局锁 ,不能提交本地事务。
  • 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
    在这里插入图片描述
    在这整个过程中全局事务2都没能拿到 全局锁,也就没能提交全局事务,所以不会发生 脏写 的问题。
5.1.4 读隔离

在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)。如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
在这里插入图片描述
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

5.1.5 AT模式的工作流程

以更新 person 业务表的数据为例:update person set age = 18 where name = 'Tom';

一阶段:

  1. 解析 SQL:得到 SQL 的类型(UPDATE),表(person),条件(where name = ‘Tom’)等相关的信息。
  2. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。select id, name, since from product where name = 'TXC';
  3. 执行业务 SQL:更新这条记录的 age 为 18。
  4. 查询后镜像:根据前镜像的结果,通过 主键 定位数据。select id, name, age from person where id = 1;
  5. 插入回滚日志:把前、后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。
  6. 提交本地事务前,向 TC 注册分支:申请 person 表中,主键值等于 1 的记录的 全局锁 。
  7. 拿到全局锁后,提交本地事务。
  8. 将本地事务提交的结果上报给 TC。

二阶段–回滚:

  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  3. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句。
  5. 提交本地事务,并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

二阶段–提交:

  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。
5.1.6 XA模式实现
  1. 添加配置seata:data-source-proxy-mode: AT
  2. 在需要分布式事务的业务代码上添加注解 @GlobalTransactional
5.1.7 AT模式的优缺点

优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能比较好。
  • 没有代码侵入,框架自动完成回滚和提交。
  • 使用方便,在需要分布式事务的业务代码上添加注解@GlobalTransactional即可。

缺点:

  • 属于最终一致,对一致性的要求相对较低,中间可能会出现数据不一致的情况。
  • 由于AT模式依赖于本地事务,所以受限于本地事务管理器支持的隔离级别,可能无法满足某些特定的隔离需求。
5.2 TCC模式

TCC模式下,Seata也是在业务层面实现的二阶段提交方案,不过AT模式不同的是,TCC模式不再依赖本地事务,而是通过人工编码定义一个接口,接口中包含三个方法,供每个分支事务来实现各种的提交和回滚逻辑。因此,会有业务代码侵入。

// 定义一个全局事务的接口
public interface IGlobalService {
// 尝试预留或锁定资源
boolean tryTransfer();
// 最终的确认操作
boolean confirmTransfer();
// 最终的回滚操作
boolean cancelTransfer();
}
5.2.1 整体机制

也是基于两阶段提交:

  • 一阶段:尝试阶段,各个参与者尝试预留或锁定资源。
  • 二阶段:
    • 确认阶段,各个参与者进行最终的确认操作。
    • 取消阶段,各个参与者进行最终的确认操作。
5.2.2 TCC模式的工作流程

一阶段:
Try阶段(尝试阶段):在这个阶段,各个参与者尝试预留或锁定资源,并执行必要的前置检查。如果所有参与者的Try操作都成功,表示资源可用,并进入下一阶段。如果有任何一个参与者的Try操作失败,表示资源不可用或发生冲突,事务将中止。

二阶段:
Confirm阶段(确认阶段):在这个阶段,各个参与者进行最终的确认操作,将资源真正提交或应用到系统中。如果所有参与者的Confirm操作都成功,事务完成,提交操作得到确认。如果有任何一个参与者的Confirm操作失败,事务将进入Cancel阶段。

二阶段:
Cancel阶段(取消阶段):在这个阶段,各个参与者进行回滚或取消操作,将之前尝试预留或锁定的资源恢复到原始状态。如果所有参与者的Cancel操作都成功,事务被取消,资源释放。如果有任何一个参与者的Cancel操作失败,可能需要进行补偿或人工介入来恢复系统一致性。

5.2.3 用例实现
  1. 分支事务具体实现
public class BranchServiceImpl implements IGlobalService {
    @Override
    public boolean tryTransfer() {
        // 尝试预留或锁定资源
        // 如果成功,返回 true;如果失败,返回 false
    }
    @Override
    public boolean confirmTransfer() {
        // 最终的确认操作
        // 如果成功,返回 true;如果失败,返回 false
    }
    @Override
    public boolean cancelTransfer() {
        // 进行回滚或取消操作
        // 如果成功,返回 true;如果失败,返回 false
    }
}
  1. 客户端调用
String xid = RootContext.getXID();
// 开启全局事务
TransactionContext context = new TransactionContext();
context.setXid(xid);
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
// 调用参与者的tryTransfer方法
boolean tryResult = branchServiceImpl.tryTransfer();
if (tryResult) {
// 提交全局事务
tx.commit();
} else {
// 回滚全局事务
tx.rollback();
}
} catch (Exception e) {
// 异常时回滚全局事务tx.rollback();
}
5.2.4 TCC模式的优缺点

优点:

  • 一阶段直接提交事务,释放数据库资源,性能好;
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强;
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库;

缺点:

  • 有代码侵入,需要人为的设计和编写try、Confirm和Cancel接口,太麻烦;
  • 软状态,事务是最终一致;
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理;
5.3 SAGA模式

Saga模式是将一个长事务分解为多个小的、可逆的事务片段,每个事务片段都是一个真实的本地事务。每个事务片段都有对应的补偿动作,补偿动作用于撤销因事务片段执行所造成的结果。
在这里插入图片描述

5.3.1 整体机制

Saga模式的提交过程也分为两个阶段:

  • 一阶段:直接提交子事务
  • 二阶段:成功则什么都不做;失败则执行补偿业务来回滚;
5.3.2 saga模式的工作流程

一阶段:执行正向操作

  1. 按照事务的逻辑顺序,依次执行正向操作。每个正向操作都会记录事务的执行状态。
  2. 如果所有的正向操作都成功执行,则事务提交完成。
  3. 如果某个正向操作失败,将会触发相应的补偿操作。

二阶段:执行补偿操作

  1. 按照逆序依次执行已经触发的补偿操作。补偿操作应该具备幂等性,以便可以多次执行而不会造成副作用。
  2. 如果所有的补偿操作都成功执行,则事务回滚完成。
  3. 如果补偿操作也失败,需要人工介入或其他手段来解决事务的一致性问题。
5.3.3 saga模式的优缺点

优点:

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高;
  • 一阶段直接提交事务,无锁,性能好;
  • 补偿服务易于实现,不用编写TCC中的三个阶段,实现简单

缺点:

  • 软状态持续时间不确定,时效性差;
  • 没有锁,没有业务层面的事务隔离,会有脏写
5.4 XA模式

XA模式是事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。具有强一致性,牺牲了一定的可用性,无业务侵入。
在这里插入图片描述

5.4.1 整体机制

XA模式的提交过程也分为两个阶段:

  • 一阶段:准备阶段,各个分支事务执行完本地事务,但不提交本地事务。
  • 二阶段:
    • 提交阶段,各个分支事务同时进行提交本地事务操作。
    • 中断阶段,各个分支事务同时进行回滚本地事务操作。
5.4.2 XA模式的事务执行流程

一阶段:准备阶段

  1. 每个分支事务的RM注册分支事务到TC;
  2. 执行分支业务sql但不提交,继续持有数据库的锁;
  3. RM报告执行状态到TC;

二阶段:提交阶段

  1. TC检测各分支事务执行状态全部为成功;
  2. 通知所有RM提交本地事务;
  3. RM接收TC的通知,提交分支事务,并释放之前持有的数据库锁;

二阶段:中断阶段

  1. TC检测所有分支事务执行状态中有失败的;
  2. 通知所有RM回滚本地事务;
  3. RM接收TC的通知,回滚分支事务,并释放之前持有的数据库锁;
5.4.3 XA模式实现
  1. 添加配置seata:data-source-proxy-mode: XA
  2. 在需要分布式事务的业务代码上添加注解 @GlobalTransactional
5.4.4 XA模式的优缺点

优点:

  • 通过XA 协议的机制来管理分支事务实现简单,并且没有代码侵入;
  • 事务具有强一致性,确保所有参与者要么一起提交事务,要么一起中断事务;

缺点:

  • 必须依赖于支持XA 事务的数据库;
  • 由于一阶段不提交本地事务,所有的参与者都需要等待事务协调器的指令来进行提交或者回滚,期间可能造成阻塞,影响系统的吞吐量和并发性能。

六、四种模式对比

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1261509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

错误:FinalShell连接CentOs连接失败

需要说明的是:这个错误不是首次连接发生的,而是多次使用后可能发生的错误 正文: 可能的原因是虚拟机的ip地址发生了变更,原因有以下几点: 最最可能的原因:1.DHCP分配变更: 如果虚拟机使用DHCP来获取IP地址,那么DHCP服务器可能会分配给虚拟机一个新的I…

java设计模式学习之【单例模式】

文章目录 引言单例模式简介定义与用途实现方式:饿汉式懒汉式 UML 使用场景优势与劣势单例模式在spring中的应用饿汉式实现懒汉式实现数据库连接示例代码地址 引言 单例模式是一种常用的设计模式,用于确保在一个程序中一个类只有一个实例,并且…

不小心删除了短信,如何在 Android 上恢复已删除的短信

不小心删除了文字消息在 Android 手机上使用可能会是一种令人痛苦的体验。这些消息可能包含有价值的信息、珍贵的回忆或重要的细节。幸运的是,您可以探索多种方法来恢复这些丢失的消息。在本文中,我们将深入研究可用于检索已删除短信的选项,并…

vue3中readonly和shallowReadonly

readonly: 深度只读数据 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。 只读代理是深层的:访问的任何嵌套 property 也是只读的。 shallowReadonly 浅只读数据 创建一个代理,使其自身的 property 为只读,但不执行…

WhatsApp API号解封教程(内含图片指引和申诉模板)

WhatsApp API 是专门为中大型企业设置的WhatsApp APP页面,API号并不像WhatsApp个人号和企业号一样可以直接从App Store 或Google Play 下载,而是需要对接官方来连接API。 虽然申请WhatsApp API号的程序和手续比较复杂,但是这个操作对于企业来…

算法通关村第二关—手写链表反转(青铜)

链表反转的三种方式 一、建立虚拟头结点辅助反转 为了方便反转,可以创建一个ans结点,让ans.next head,然后后面的结点一次插入到ans.next 在下图中,对(1->2->3->4->5)进行反转,可以创建ans&…

easyexcel指定sheet页动态给行列加背景色

easyexcel,有多个sheet页,某些sheet页的行、列动态需要加背景色 import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.m…

Spring Cloud,注册中心,配置中心,原理详解

文章目录 Spring Cloud,注册中心,配置中心,原理详解谈谈我个人对 spring Cloud 的理解 注册中心Eureka:服务搭建小结 Ribbo - 负载均衡1. 负载均衡流程2. 负载均衡策略 nacos注册中心1. 配置集群1. 创建 namespace2. 配置命名空间…

MATLAB实战 | 不同形式的三维曲面图

通常,MATLAB中绘制三维曲面图,先要生成网格数据,再调用mesh函数和surf函数绘制三维曲面。若曲面用含两个自变量的参数方程定义,则还可以调用fmesh函数和fsurf函数绘图。若曲面用隐函数定义,则可以调用fimplicit3函数绘…

RuoYi若依前后端分离框架二次开发基础项目

一键改标题:点击ruoyi-ui(ctrlshiftr)改完重启项目 连接本地数据库 全智能生成开发 新建maven项目 菜单创建---栏目创建 无权限删除时:去掉要删除的菜单权限 子菜单:新增 代码生成器cv进去 后端: 前端: 完成&#x…

人工智能与供应链行业融合:预测算法的通用化与实战化

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 让我们一起深入探索人工智能与供应链的融合,以及预测算法在实际应用中的价值!🔍🚀 文章目录 前言供应链预测算法的基本流程统计学习模型与机…

顺子日期(14)

顺着日期 public class Main {public static void main(String[] args) {int res 0;//2022年int[] days new int[] {31,28,31,30,31,30,31,31,30,31,30,31};//31,28,31,30,31,30,31,31,30,31,30,31//一三五七八十腊//构造2022年每一天的日期yyyymmddStringBuffer date new…

现在的教师工资这么低了?

最近刷v站看到个帖子t/991351 ,程序员转行回乡当老师月薪才2000,现在的教师工资这么低了?月薪3000都不到。 搜索了下,现在有些地方开始#教师全员竞聘上岗# https://weibo.com/1784473157/NpUd3esCv ,曾经的铁饭碗也可能…

【开发问题解决方法记录】02.dian

想重命名表名,但是失败了,提示[0A000][3001] ORA-03001: 未实施的功能 Position: 0. 获取到USER_ID和ROLE_ID但是无法新增成功 问题出在哪里捏? 报错:ORA-06502: PL/SQL: 数字或值错误 : 字符到数值的转换错误 展示的是…

不受平台限制,Sketch 网页版震撼登场

Sketch 是一种基于 Mac 的矢量图形编辑器,可用于数字设计。其主要功能包括无损矢量编辑、完美像素精度和数百个插件同步功能,可导出预设和代码。它是目前流行的页面交互协作设计工具。但是 Sketch 最大的缺点是对 Windows/PC 用户不友好。严格来说&#…

笔记十七、认识React的路由插件react-router-dom和基本使用

react-router 分类 web使用 react-router-dom native使用 react-router-native anywhere(使用麻烦) react-router 安装 yarn add react-router-dom main.jsx import React from "react"; import ReactDOM from "react-dom/client"…

宋仕强论道之华强北曼哈商城(十二)

宋仕强论道之华强北曼哈商城(十二): 讲了华强北万佳百货以后要讲旁边配套的商场。在1995年,在万佳百货旁边开了一个曼哈商城,名字叫曼哈是因为老板是美国纽约曼哈顿留学回来的,是一个叫张虹女孩子。华强北曼…

DS图—图的最短路径/Dijkstra算法【数据结构】

DS图—图的最短路径/Dijkstra算法【数据结构】 题目描述 给出一个图的邻接矩阵,输入顶点v,用迪杰斯特拉算法求顶点v到其它顶点的最短路径。 输入 第一行输入t,表示有t个测试实例 第二行输入顶点数n和n个顶点信息 第三行起,每行…

C/C++字节对齐

C/C字节对齐 C/C字节对齐1.G_PACKED2.1 pack(push)2.2 pack(1) 全部例子 C/C字节对齐 1.G_PACKED #ifdef __GNUC__#define G_PACKED( __Declaration__ ) __Declaration__ __attribute__((packed)) #else#define G_PACKED( __Declaration__ ) __pragma( pack(push,1)) __Decla…

万宾科技第四代可燃气体监测仪的作用

燃气作为一种重要的能源已在居民生活、工业生产和商业活动等领域得到了广泛的应用。但是与之而来的便是各种各样的燃气管网的安全问题,其中燃气管网泄漏成为了城市生命线建设中亟待解决的安全隐患。因此采取切实有效的措施来保障燃气管网的安全运行,应用…