seata分布式事务解决方案

seata分布式事务解决方案下载地址 Releases seata seata GitHubSeata 介绍 Seata 是一款开源的分布式事务解决方案 致力于提供高性能和简单易用的分布式事务服务 Seata 将为用户提供了 AT TCC SAGA 和 XA 事务模式 为用户打造一站式的分布式解决方案 SeataATSeata 的 AT 模式 AutomaticTra 是一种无侵入的分布式事务解决方案 SeataAT 基本原理 SeataAT 事务分两个阶段来管理

下载地址:Releases · seata/seata · GitHub

本帖项目Git地址

一、什么是分布式事务

首先这是普通事务:

事务

下面是分布式事务:

分布式事务

在微服务系统中,每个微服务应用都可能会有自己的数据库,它们首先需要控制自己的本地事务。

一项业务操作可能会调用执行多个微服务。如何保证多个服务执行的多个数据库的操作整体成功或整体失败?这就是分布式事务要解决的问题。

二、Seata介绍

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

三、Seata AT

Seata 的 AT 模式(Automatic Transaction)是一种无侵入的分布式事务解决方案。

1、业务场景订单系统

业务

当用户下订单时,执行以下三步流程:

  1. 订单系统保存订单
  2. 订单系统调用库存服务,减少商品库存
  3. 订单系统调用账户服务,扣减用户金额

这三步要作为一个整体事务进行管理,要么整体成功,要么整体失败。

2、Seata AT基本原理

第一阶段:执行各分支事务

微服务系统中,各服务之间无法相互感知事务是否执行成功,这时就需要一个专门的服务,来协调各个服务的运行状态。这个服务称为 TC(Transaction Coordinator),事务协调器。

tc

订单系统开始执行保存订单之前,首先启动 TM(Transaction Manager,事务管理器),由 TM 向 TC 申请开启一个全局事务:

TM

这时TC会产生一个全局事务ID,称为 XID,并将 XID 传回 TM:

xid

这样就开启了全局事务

全局事务开启后,开始执行创建订单的业务。首先执行保存订单,这时会先启动一个 RM(Resource Manager,资源管理器),并将 XID 传递给 RM。

rm

RM 负责对分支事务(即微服务的本地事务)进行管理,并与 TC 通信,上报分支事务的执行状态、接收全局事务的提交或回滚指令。

RM 首先会使用 XID 向 TC 注册分支事务,将分支事务纳入对应的全局事务管辖。

rm

现在可以执行保存订单的分支事务了。一旦分支事务执行成功,RM 会上报事务状态:

tx

TC 收到后,会将该状态信息传递到 TM:

o

到此,保存订单过程结束。

相同的,完成其他模块分支事务: 

a

第二阶段: 控制全局事务最终提交或回滚

全局事务最终提交

tc

全局事务最终回滚

tc

3、Seata AT具体工作机制 

第一阶段:执行分支事务

以全面订单业务中的库存服务为例,库存表中存在一条商品的库存信息:

s

现在要执行业务操作减少库存,从50件减少到40件:

s

执行修改库存业务操作前, 会先取出旧的库存信息进行保存:

s

现在可以修改库存了:

s

接着,取出更新后的新数据:

s

接下来,会把旧数据和新数据合并起来,保存到一个事务回滚日志表:undo_log表:

s

至此,第一阶段,分支事务完成,将状态上报给TC: 

第二阶段:控制全局事务最终提交或者最终回滚

全局事务最终提交

如果全局事务成功,要完成最终提交,AT模式最终提交操作非常简单,只需要删除日志数据即可。首先接收到 TC 的全局事务提交指令:

s

然后直接删除事务日志,就完成了第二阶段提交操作:

 a

 全局事务最终回滚

假如全局事务失败,那么第一阶段已提交的分支事务要执行回滚操作。首先会收到来自 TC 的全局事务回滚指令:

s

根据事务回滚日志(undo_log)表的记录,将商品恢复成旧的库存数据:

s

然后删除事务日志,最终完成第二阶段回滚操作:

s

4、简单理解工作机制

订单、库存、账户这三个模块数据相互关联,应当同时成功,当一个模块失败,需要3模块一起执行回滚,保证数据同步。

当模块向数据库执行更新操作,他会自动先执行查询语句,将该表存入日志,每个模块都做同样的事。当事务执行成功,删除日志。 当事务执行失败,执行集体回滚,将日志中的内容覆盖各自的表,然后散出日志记录。

5、seataAT配置步骤

 1、修改下载的seata中的文件配置

 registry.conf 向 eureka 注册

seata分布式事务解决方案

file.conf seata server运行过程中产生的日志数据,存储到什么位置 

seata分布式事务解决方案

seata分布式事务解决方案

修改启动文件:seata-server.bat

用文本编辑器打开文件,找到文件中这一行:

%JAVACMD% %JAVA_OPTS% -server -Xmx256m -Xms256m -Xmn128m -Xss512k -XX:Sur……

2、运行 seata-server.bat 启动服务

在bin目录下输入启动命令:seata-server.bat

注意事项:

3、AT项目中项目实战配置(调用和被调用都要添加)

Ⅰ、在需要管理的事务模块中添加 seata 依赖

 
    
    
      com.alibaba.cloud 
     
    
      spring-cloud-alibaba-seata 
     
    
      ${spring-cloud-alibaba-seata.version} 
     
     
      
      
        seata-all 
       
      
        io.seata 
       
      
     
    
    
    
      io.seata 
     
    
      seata-all 
     
    
      ${seata.version} 
     
   

spring cloud: alibaba: seata: tx-service-group: order_tx_group

与seata中的registry.conf 文件相对应

同理

数据源:数据源是指数据库应用程序所使用的数据库或者数据库服务器。数据源(Data Source)顾名思义,数据的来源,是提供某种所需要数据的器件或原始媒体。在数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接。

seata分布式事务解决方案

import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; @Configuration public class DSAutoConf { // 创建原始数据源 @ConfigurationProperties(prefix = "spring.datasource") //配置文件中数据库连接配置 @Bean public DataSource getDataSource() { return new HikariDataSource(); } // 创建数据源代理 @Primary // 由于上下两个名字冲突,该注解表示spring的首选对象 @Bean public DataSource getDataSourceProxy(DataSource ds) { return new DataSourceProxy(ds); } } 
spring: application: name: order datasource: url: jdbc:mysql:///seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root jdbcUrl: ${spring.datasource.url}

seata分布式事务解决方案

Ⅶ、@GlobalTransactional — 启动全局事务,只在主调用模块添加

在启动类上

seata分布式事务解决方案

四、Seata TCC

1、TCC 基本原理

TCC 与 Seata AT 事务一样都是两阶段事务,它与 AT 事务的主要区别为: 

  • TCC 对业务代码侵入严重
    每个阶段的数据操作都要自己进行编码来实现,事务框架无法自动处理。

  • TCC 效率更高
    不必对数据加全局锁,允许多个事务同时操作数据。

  • 有些业务AT完成不了,需要TCC

a

1、第一阶段 Try 

当下订单时要扣减用户账户金额:

a

假如用户购买 100 元商品,要扣减 100 元。TCC 事务首先对这100元的扣减金额进行预留,或者说是先冻结这100元:

a

2、第二阶段 Confirm

如果第一阶段能够顺利完成,那么说明“扣减金额”业务(分支事务)最终肯定是可以成功的。当全局事务提交时, TC会控制当前分支事务进行提交,如果提交失败,TC 会反复尝试,直到提交成功为止。当全局事务提交时,就可以使用冻结的金额来最终实现业务数据操作:

a

3、第二阶段 Cancel 

如果全局事务回滚,就把冻结的金额进行解冻,恢复到以前的状态,TC 会控制当前分支事务回滚,如果回滚失败,TC 会反复尝试,直到回滚完成为止。

a

4、多个事务并发的情况 

多个TCC全局事务允许并发,它们执行扣减金额时,只需要冻结各自的金额即可:

a

2、TCC 事务模式,需要以下组件来支持全局事务的控制:

  • TC 事务协调器
  • TM 事务管理器
  • RM 资源管理器

3、TCC项目中项目实战配置

①seata 依赖

②三个配置文件,与 AT 事务完全相同

③修改 Mapper,添加新的数据操作

④按照 seata 的规则,添加 TccAction 接口和实现,使用 Mapper 完成 TCC 数据库操作

import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.LocalTCC; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import java.math.BigDecimal; /* * 定义三个操作的方法是否执行成功 * T 冻结方法 * C 提交方法 * C 回滚方法 * */ @LocalTCC public interface OrderTccAction { //该注解所在的方法是T(冻结数据)方法 @TwoPhaseBusinessAction(name = "OrderTccAction" , // name:一般为接口名称 commitMethod ="commit", //commitMethod:确认提交的方法名,默认commit可以省略 rollbackMethod = "rollback") // rollbackMethod:表示回滚方法,默认rollback可以省略 boolean prepare(BusinessActionContext ctx, //BusinessActionContext 上下文对象,在一二阶段之间传递数据 @BusinessActionContextParameter(paramName = "orderId") Long orderId,//第二阶段使用第一阶段的orderId,通过注解放入上下文对象BusinessActionContext中 Long userId, Long productId, Integer count, BigDecimal money);//为了避免seata的bug,将数据一个个单传,不能封装为对象。 boolean commit(BusinessActionContext ctx); boolean rollback(BusinessActionContext ctx); }

⑤修改业务方法,调用 TccAction 的第一阶段方法来冻结数据

seata分布式事务解决方案

⑥在第一个模块上添加 @GlobalTransactional 启动全局事务

seata分布式事务解决方案

4、遇到的问题

  • 如果第一阶段冻结数据失败,仍然会执行二阶段的回滚操作
  • 第二阶段失败,TC会重复下发二阶段执行指令,重复执行二阶段的数据操作

解决方法:幂等性控制,重复操作时,和一次操作结果相同

具体实施步骤

总体概述:

向事务中添加标记,标记为一个map集合的嵌套。

Map 
  
    , Map 
   
     > //该分布式模块.class 事务id(在上下文对象中获取) 标记(自定义String类型作为标记) 
    
  

一阶段完成没有出错,添加一个标记,二阶段进行标记验证,如果没有标记,直接返回true结束程序,如果有标记,当二阶段执行完成时,删除标记,完美解决了前两个问题

步骤一:标记类的创建

import java.util.Map; import java.util.concurrent.ConcurrentHashMap; //标记处理类 public class ResultHolder { //该分布式模块.class 事务id(在上下文对象中获取) 标记(自定义String类型作为标记) private static Map 
  
    , Map 
   
     > map = new ConcurrentHashMap 
    
      , Map 
     
       >(); public static void setResult(Class 
       actionClass, String xid, String v) { Map 
      
        results = map.get(actionClass); if (results == null) { synchronized (map) { if (results == null) { results = new ConcurrentHashMap<>(); map.put(actionClass, results); } } } results.put(xid, v); } public static String getResult(Class 
        actionClass, String xid) { Map 
       
         results = map.get(actionClass); if (results != null) { return results.get(xid); } return null; } public static void removeResult(Class 
         actionClass, String xid) { Map 
        
          results = map.get(actionClass); if (results != null) { results.remove(xid); } } } 
         
        
       
      
     
    
  

步骤二:在阶段一做标记的添加

seata分布式事务解决方案

 步骤三:在第二阶段使用标记

seata分布式事务解决方案

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227234.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午9:38
下一篇 2026年3月16日 下午9:39


相关推荐

  • Java Socket实现多人聊天系统(图形界面+文件传输功能)

    Java Socket实现多人聊天系统(图形界面+文件传输功能)文章目录前言一 多人聊天系统 1 1 客户端 1 2 服务端前言 GitHub 地址 https github com Samven7 mutichat system 一 多人聊天系统 1 1 客户端 Login java 登录界面 Login javapackagee importjava awt importjavax swing publicclass

    2026年3月18日
    1
  • 知识库名称_个人知识库

    知识库名称_个人知识库创作歌剧《魔笛》的是::莫扎特春分时黄昏时北斗星指向::东方春秋战国时期,法家的代表人是::韩非子春秋战国时期与伯乐齐名的相马高人::方九皋国画名品《泼墨仙人》是谁的作品?:梁楷国际奥林匹克集邮联合会在哪一个国家成立?:瑞士国际奥委会发行的《祝贺北京成为2008年奥林匹克运动会主办城市》的纪念封上,代表北京的图案是::天坛国际奥委会评估团2月底对北京进行

    2026年4月15日
    8
  • Visual Studio 2017 – Windows应用程序打包成exe文件(2)- Advanced Installer 关于Newtonsoft.Json,LINQ to JSON的一个小d…

    Visual Studio 2017 – Windows应用程序打包成exe文件(2)- Advanced Installer 关于Newtonsoft.Json,LINQ to JSON的一个小d…

    2021年6月9日
    176
  • 金蝶erp管理系统下载_erp 金蝶

    金蝶erp管理系统下载_erp 金蝶源码说明:超强的工作流系统,强大的可视化工作流编辑器,灵活自定义表单设计,充分实现企业基于工作流的各项业务管理,与其他模块数据(如HRM等)完美结合。全面综合的任务系统,实现基于信息录入、单据管理、业务协作、工作流转、任务提醒、统计分析等的智能管理。所有单据均支持上传附件功能;且报价单、销售合同、采购订单、发货出库、入库单、派工单等所有模板均支持自定义编辑打印。集成功能实用的CRM系统、进…

    2025年12月3日
    6
  • 关于心跳ajax请求pending状态(被挂起),stalled时间过长的问题。涉及tcp连接异常。[通俗易懂]

    关于心跳ajax请求pending状态(被挂起),stalled时间过长的问题。涉及tcp连接异常。[通俗易懂]环境:景安快云服务器(听说很垃圾,但是公司买的,我也刚来),CentOS6.8x86_64,Apache,MySQL5.1,PHP5.3.问题:现公司有一个php系统,需要重复向后台发送ajax

    2022年8月2日
    10
  • 获取当前jar包路径_java获取jar文件

    获取当前jar包路径_java获取jar文件一、获取可执行jar包所在目录(1)方法一:使用System.getProperty(“java.class.path”)获取classpath的路径,若没有其他依赖,在cmd下运行该可执行jar包,则该值即为该jar包的绝对路径。代码如下:/***方法一:获取当前可执行jar包所在目录*/StringfilePath=System.getProperty(“java.class.

    2026年4月16日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号