当前位置: 首页 > news >正文

Seata分布式事务

目录

  • 概念
    • 本地事务
    • 分布式事务
      • 全局事务
      • 分支事务
      • 全局事务和分支事务的关系
  • Seata
    • Seata解决分布式事务的思路
    • Seata部署服务器端(TC)
    • 微服务整合Seata客户端
    • XA模式
      • 工作流程
      • XA 模式配置与使用
      • XA 模式优缺点
    • AT模式
      • 工作流程
      • AT 模式优缺点
      • AT 模式配置与使用
    • TM和RM的交互

概念

本地事务

  • 在传统的单体应用中,我们通常使用​​本地事务​​来保证数据一致性。本地事务依赖于关系型数据库本身的ACID特性(原子性、一致性、隔离性、持久性),所有操作都在同一个数据库连接中完成,要么全部成功,要么全部失败

分布式事务

  • 随着互联网技术的快速发展,​​软件系统从单体应用转变为分布式应用​​。在微服务架构中,一个业务需要联调多个微服务实现,每个微服务都是其中一个环节,也都有自己的数据库事务。所以,分布式事务简单来说就是一个业务涉及的多个微服务事务要保证ACID属性,这些事务要么同时成功,要么同时失败。
  • 举一个简单的例子,订单微服务和库存微服务,下单的同时订单微服务需要远程调用库存微服务进行减库存操作,要保证订单服务和库存服务对数据库的操作同成功,或者同失败。
  • 当然,分布式事务也有另外两个场景,一个服务连接多个数据库,多个微服务连接同一个数据库,这些都是分布式事务的应用场景,解决思路和措施都是一样的。

在分布式事务处理中,有两个核心概念:​​全局事务​​和​​分支事务​​。

全局事务

  • 全局事务​​是指​​整个分布式事务的范围​​,它是由一系列分布在多个微服务上的操作组成的逻辑单元。全局事务需要保证所有这些操作​​要么全部成功,要么全部失败​​,以维护分布式系统的数据一致性。
  • 在Java微服务场景中,全局事务通常由一个​​事务管理器(Transaction Manager, TM)​​ 来定义和控制。TM负责界定全局事务的边界(开始、提交或回滚),并根据所有分支事务的执行状态做出最终决策。

分支事务

  • 分支事务​​是​​全局事务中的一个组成部分​​,通常是每个微服务执行的​​本地事务
  • 每个分支事务负责管理其自身的资源(如数据库),并向全局事务协调者(TC)报告自己的执行状态

全局事务和分支事务的关系

  • 全局事务和分支事务是​​整体与部分​​的关系。一个全局事务包含多个分支事务,全局事务的状态取决于所有分支事务的执行结果。它们共同协作,确保分布式系统中的数据操作满足原子性要求。

Seata

Seata 是一款由阿里开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

Seata解决分布式事务的思路

  • 其实分布式事务产生的一个重要原因,就是参与事务的多个分支事务互相无感知,不知道彼此的执行状态。因此解决分布式事务的思路就是找一个统一的事务协调者(TC),与多个分支事务通信(RM),检测每个分支事务的执行状态,保证全局事务下的每一个分支事务同时成功或失败即可。
  • Seata也不例外,在Seata的事务管理中有三个重要的角色
角色作用
TC-事务协调者维护全局和分支事务的状态,协调全局事务提交或回滚。
TM-事务管理器定义全局事务的范围、开始全局事务、提交或回滚全局事务
RM-资源管理器管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

在这里插入图片描述

  • 其中,TM和RM可以理解为Seata的客户端部分,引入到参与事务的微服务依赖中即可。将来TM和RM就会协助微服务,实现本地分支事务与TC之间交互,实现事务的提交或回滚。
  • 而TC服务则是事务协调中心,是一个独立的微服务,需要单独部署。

Seata部署服务器端(TC)

TC Server 的部署模式主要分两种:

  • ​​file 模式​​:事务会话信息存储在本地文件系统中,​​仅适用于测试和学习
  • db 模式​​:事务会话信息持久化到共享数据库中,​​适用于生产环境​​,支持 TC 集群和高可用。

这篇博客主要讲下db模式的部署,这也是TC Server 一般的部署方式,值得说明的是,TC Server也是一个springboot微服务,部署方式与其他boot项目没什么区别,无非就是改改配置文件。

  1. 下载 Seata Server​
    从 Seata 官网的下载页面(https://seata.apache.org/zh-cn/download/seata-server)获取最新版本的 seata-server-*.zip包并解压到非中文目录
  2. 准备数据库(以mysql为例)
    创建一个专门用于Seata的数据库(如seata);
    执行 Seata 发行包中 script/server/db/mysql.sql的 SQL 脚本,创建全局事务表 global_table、分支事务表 branch_table和全局锁表 lock_table。
  3. 添加nacos seata配置
    你也可以把seata相关配置全配置到application配置文件中,这样就无需从nacos配置中心拉取配置,但是一般生产环境中,seata TC服务器在一个集群中会有很多实例,这些实例共用一个数据库资源(第2步配置的),因此避免重复配置,最好seata配置放在nacos中,避免所有seata TC重复配置。

在 Nacos 控制台 (http://127.0.0.1:8848/nacos) 中,创建一个新的配置,yaml或者properties文件格式都可以

# 数据存储模式,db代表使用数据库进行持久化(生产环境推荐)
store:mode: dbdb:datasource: druiddbType: mysql# 根据你的MySQL版本选择驱动driverClassName: com.mysql.cj.jdbc.Driver # MySQL 8.x# driverClassName: com.mysql.jdbc.Driver # MySQL 5.xurl: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=UTCuser: rootpassword: your_strong_password_here # 请替换为你的数据库密码minConn: 5maxConn: 30globalTable: global_tablebranchTable: branch_tablelockTable: lock_tablequeryLimit: 100maxWait: 5000
  1. 修改本地配置:连接注册与配置中心
    修改本地application配置文件,配置nacos注册中心和配置中心相关配置,目的是让本地Seata Server将服务ip,端口,名称空间,group(组),集群信息注册到nacos注册中心,方便以后Seata 客户端调用;其次就是从指定的nacos配置中心的特定组(group)中拉取本地Seata Server相关的运行配置,主要是数据库信息
# 示例: Seata 1.5.0+ 的 application.yml 部分配置
seata:config:type: nacos # 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取nacos:server-addr: 127.0.0.1:8848 # Nacos服务器地址group: SEATA_GROUP           # 存储Seata配置的Group,通常为 SEATA_GROUPdataId: seataServer.properties # 在Nacos中存储配置的文件名registry:type: nacos # tc服务的注册中心类,这里选择nacosnacos:application: seata-tc-server # TC服务在Nacos中注册的服务名称,可自定义server-addr: 127.0.0.1:8848group: DEFAULT_GROUPnamespace: ""                 # Nacos命名空间ID,默认为空cluster: SH                   # TC集群名称,可自定义(如SH, HZ)
  1. 启动 TC Server
    进入 Seata 解压目录的 bin文件夹,执行启动脚本:
    Linux/Unix​​: sh seata-server.sh
    ​​Windows​​: 双击运行 seata-server.bat

微服务整合Seata客户端

  1. 引入客户端依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><version>2.2.1.RELEASE</version> <!-- 请选择与你的Spring Cloud Alibaba版本兼容的Seata版本 -->
</dependency>
  1. ​​配置 Seata 客户端​​:在 yaml中进行配置,确保与服务端(TC)连接
seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址type: nacos # 注册中心类型 nacosnacos:server-addr: 192.168.150.101:8848 # nacos地址namespace: "" # namespace,默认为空group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUPapplication: seata-server # seata服务名称username: nacospassword: nacostx-service-group: hmall # 事务组名称service:vgroup-mapping: # 事务组与tc集群的映射关系hmall: "SH"  # TC集群名称
  1. ​​创建 undo_log表​​:在每个​​参与分布式事务的业务的数据库中​​,都需要创建 undo_log表,用于 Seata AT 模式记录回滚日志。
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

XA模式

Seata 的 XA 模式和 AT 模式是两种主流的分布式事务解决方案,它们在实现机制、一致性保证和适用场景上各有特点,XA 模式和AT模式都是采用传统的两阶段提交(2PC)协议来实现​​强一致性
在这里插入图片描述

工作流程

  1. 第一阶段 - 执行阶段 (Prepare Phase)
  • TM 向 TC 申请开启一个全局事务。
  • TM 调用各个分支事务(RM)
  • 每个 RM 在本地执行业务 SQL,但​​并不提交​​,而是等待 TC 的进一步指令。在此期间,相关数据库资源会被​​锁定​​
  • RM 将本地事务的执行状态(成功或失败)报告给 TC
  1. 第二阶段 - 提交/回滚阶段 (Commit/Rollback Phase)​​
  • 提交​​:如果 TC 收到所有 RM 的“成功”报告,则向所有 RM 发送提交指令。各 RM 接收到指令后提交本地事务,并释放资源锁。
  • 回滚​​:如果 TC 收到任何一个 RM 的“失败”报告,则向所有 RM 发送回滚指令。各 RM 接收到指令后回滚本地事务,并释放资源锁

XA 模式配置与使用

  1. Application.yml 配置
    关键是指定 data-source-proxy-mode: XA。
seata:enabled: trueapplication-id: ${spring.application.name}tx-service-group: my_xa_tx_group # 事务组名称,可自定义data-source-proxy-mode: XA # 指定使用XA模式
  1. 代码中使用
  • ​​事务发起方 (T M) :在全局事务入口方法上添加 @GlobalTransactional。
  • ​​事务参与者 (RM)​​ :​​无需​​在本地方法上添加 @Transactional注解,因为事务由 Seata 通过 XA 协议管理。
@Service
public class OrderServiceImpl implements OrderService {@GlobalTransactional(name = "xaCreateOrder", timeoutMills = 300000, rollbackFor = Exception.class)@Overridepublic void createOrder(Order order) {// 1. 本地操作orderMapper.insert(order);// 2. 远程调用其他服务(这些服务内的操作也会被纳入同一全局XA事务)storageFeignClient.deduct(order.getProductId(), order.getCount());accountFeignClient.debit(order.getUserId(), order.getMoney());}
}

XA 模式优缺点

  • 优点​​:保证了数据的​​强一致性​​(ACID),且对业务代码无侵入。
  • 缺点​​:​​性能较差​​。因为在第一阶段执行后到第二阶段完成前,数据库资源会一直处于锁定状态,在高并发场景下可能成为瓶颈。

AT模式

AT 模式是 Seata ​​默认​​且主推的模式。它在 XA 模式的基础上进行了优化,通过一阶段提交并生成回滚日志的方式,大大减少了资源锁定的时间,提升了性能,实现了​​最终一致性​​。

在这里插入图片描述

工作流程

  1. 第一阶段 - 执行阶段 (Prepare Phase)
  • TM 向 TC 申请开启一个全局事务。
  • TM 调用各个分支事务(RM)
  • 在执行业务 SQL 前,RM 的​​数据源代理​​会先拦截 SQL,查询数据的前镜像(Before Image),即修改前的数据状态。
  • RM 执行业务 SQL 并​​提交本地事务​​,​​立即释放本地数据库锁​​
  • 在执行业务 SQL 后,RM 会再次查询数据的后镜像(After Image),即修改后的数据状态
  • 将前后镜像信息、SQL 本身等组成​​回滚日志 (undo_log)​​,存入业务数据库的 undo_log表中,业务数据和回滚日志在同一个本地事务中提交​​
  • RM 向 TC 注册分支事务并报告执行状态。
  1. 第二阶段 -基于回滚日志的提交或回滚​​
  • 提交​​:如果所有分支事务成功,TC 会异步通知所有 RM 删除对应的 undo_log记录即可。因为一阶段已经提交,数据本身就是最终状态。
  • 回滚​​:如果任何分支事务失败,TC 会通知所有 RM 进行回滚。RM 根据 undo_log中的前镜像信息,生成逆向 SQL(如 INSERT 对应 DELETE,UPDATE 对应反向 UPDATE)并执行,将数据恢复至事务开始前的状态,然后删除 undo_log记录。

AT 模式优缺点

  • 优点​​:​​性能较好​​(一阶段即释放本地锁),对业务代码无侵入
  • 缺点​​:存在​​短暂的数据不一致​​(最终一致),且需要依赖关系型数据库和 undo_log表。

AT 模式配置与使用

yaml配置保持默认配置即可,AT模式是seata的默认工作模式

  1. 创建 undo_log表
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  1. 代码中使用
  • ​​事务发起方 (T M) :同样在全局事务入口方法上添加 @GlobalTransactional。
  • ​​事务参与者 (RM)​​ :​​每个参与者的​​本地方法​​上需要添加 ​​**@Transactional​​** 注解,以确保本地事务的开启和回滚日志的正确记录。
@Service
public class OrderServiceImpl implements OrderService {@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)@Overridepublic void createOrder(Order order) {orderMapper.insert(order); // 本地操作storageFeignClient.deduct(order.getProductId(), order.getCount()); // 远程调用}
}
// RM微服务
@Service
public class StorageServiceImpl implements StorageService {@Transactional // AT模式下,RM的本地方法需要@Transactional@Overridepublic void deduct(String commodityCode, int count) {storageMapper.reduceInventory(commodityCode, count); // 本地操作}
}

TM和RM的交互

最后再讲下TM和RM的交互流程,其他微服务的RM是如何知道自己是属于哪个全局事务的。
分支事务识别全局事务的过程,本质是 ​​XID 在分布式系统调用链中传递​​ 的过程:

  1. 生成与起始(事务发起方 - TM​​:
  • 当使用 @GlobalTransactional注解的方法被调用时,TM 会向 TC ​​申请开启一个新的全局事务​​。
  • TC 会生成一个​​全局唯一的 XID​​,并返回给 TM。
  • 这个 XID 会被设置到当前线程的上下文中
  1. ​​传播(服务调用链​​:
  • 当 TM(事务发起方服务)通过 RPC(如 Feign、Dubbo)调用其他微服务(RM)时,Seata 的客户端组件(如 Feign 拦截器)会​​自动地将当前线程上下文中的 XID 添加到 RPC 请求的 header 中​​(例如 tx_xid)
  • 这样,XID 就随着业务请求一起传递到了下游服务
  1. 接收与注册(事务参与方 - RM)
  • 下游服务(RM)的 Seata 客户端拦截器(如 Servlet 过滤器)会​​从收到的 RPC 请求的 header 中提取出 XID​​
  • 提取出的 XID 会被 ​​绑定到下游服务的当前线程上下文中
  • 当该服务执行业务方法(通常是本地数据库事务)时,其 RM 组件会​​从当前线程上下文中获取到这个 XID​​,然后带着这个 XID 向 TC ​​注册分支事务​​。注册成功后,TC 就明确知道这个分支事务属于哪个全局事务了
  • 如果这个下游服务还需要继续调用其他服务,上述​​传播过程会重复进行​​,XID 会通过 RPC Header 继续向下传递,确保整个调用链中的所有分支事务都能获取到同一个 XID。
http://www.dtcms.com/a/394570.html

相关文章:

  • 用例图讲解
  • makefile原理
  • AUTOSAR CP开发流程总结
  • 通过VNC实现树莓派远程桌面访问
  • linux信号done
  • BeanUtils.copyProperties 映射规则详解
  • 物联网 frid卡控制
  • LeetCode刷题记录----322.零钱兑换(Medium)
  • 2015/07 JLPT听力原文 问题四
  • Redis集群实验
  • 昇腾生态双支柱:MindSpore 与 CANN 的全栈技术解析
  • YOLO系列——实时屏幕检测
  • 牛客算法基础noob49 上三角矩阵判定
  • autosar 中OS模块理解
  • 通俗范畴论17.2 向量空间的对偶与双对偶
  • huggingface_hub 安装部署问题汇总
  • 在我的Java项目中为什么使用AllArgsConstructor注解注入的方式启动报错了:
  • π0:一个 VLA 流匹配模型用于通用机器人控制(又称 pi0)
  • Information theorem-Entropy
  • 编译原理实验报告——词法分析程序
  • 整体设计 完整的逻辑链条 之4 认知逻辑视角 —— 前序驱动的认知演进体系 之2
  • C/C++正则表达式PCRE2库
  • 基于python大数据的声乐信息分类评测系统
  • 永磁同步电机无速度算法--改进型超螺旋滑模观测器
  • Linux0.12的中断处理过程源码分析
  • 进程控制(Linux)
  • 【C++】——string类的使用(详细讲解)
  • 借助 Amazon ECS 全新的内置蓝绿部署功能,加速安全的软件发布进程
  • 【脑电分析系列】第24篇:运动想象BCI系统构建:CSP+LDA/SVM与深度学习方法的对比研究
  • 【论文速递】2025年第22周(May-25-31)(Robotics/Embodied AI/LLM)