news 2026/7/27 3:27:51

Seata AT模式详细实例:电商下单场景

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Seata AT模式详细实例:电商下单场景

Seata AT模式详细实例:电商下单场景

1. Seata AT模式核心原理

1.1 核心概念

  • AT模式:Automatic Transaction(自动事务),非侵入式的分布式事务解决方案
  • TC(Transaction Coordinator):事务协调器,管理全局事务状态
  • TM(Transaction Manager):事务管理器,发起和结束全局事务
  • RM(Resource Manager):资源管理器,管理分支事务,与TC交互
  • 全局事务ID(XID):唯一标识一个全局事务
  • Undo Log:回滚日志,用于事务回滚

1.2 工作流程

  1. 全局事务开启:TM向TC请求开启全局事务,获取XID
  2. 分支事务注册:RM向TC注册分支事务,关联XID
  3. SQL执行:RM执行SQL,生成Undo Log并写入数据库
  4. 分支事务提交:RM向TC报告分支事务状态
  5. 全局事务提交/回滚:TC根据分支事务状态,决定全局提交或回滚
  6. 分支事务提交/回滚:TC通知RM执行分支事务的提交或回滚

2. 项目结构设计

2.1 业务场景

电商下单流程

  • 创建订单(订单服务)
  • 扣减库存(库存服务)
  • 扣减账户余额(账户服务)

2.2 项目结构

seata-at-demo/ # 父项目 ├── order-service/ # 订单服务(8081) ├── inventory-service/ # 库存服务(8082) └── account-service/ # 账户服务(8083)

3. 环境准备

3.1 组件版本

组件版本
Spring Boot2.7.18
Spring Cloud2021.0.8
Seata1.7.1
MySQL8.0+
Nacos2.2.3

3.2 数据库初始化

3.2.1 创建Seata所需数据库和表
-- 创建seata数据库CREATEDATABASEseata;USEseata;-- 创建undo_log表(Seata AT模式必须)CREATETABLEundo_log(idBIGINTAUTO_INCREMENTPRIMARYKEY,branch_idBIGINTNOTNULL,xidVARCHAR(100)NOTNULL,contextVARCHAR(128)NOTNULL,rollback_infoLONGBLOBNOTNULL,log_statusINTNOTNULL,log_createdDATETIMENOTNULL,log_modifiedDATETIMENOTNULL,UNIQUEKEYux_undo_log(xid,branch_id));
3.2.2 业务数据库初始化

order-service数据库

CREATEDATABASEorder_db;USEorder_db;CREATETABLEorders(idBIGINTAUTO_INCREMENTPRIMARYKEY,user_idBIGINTNOTNULL,product_idBIGINTNOTNULL,countINTNOTNULL,moneyDECIMAL(10,2)NOTNULL,statusINTDEFAULT0COMMENT'0:创建中,1:已创建,2:已取消');CREATETABLEundo_log(-- 同上,每个业务库都需要undo_log表);

inventory-service数据库

CREATEDATABASEinventory_db;USEinventory_db;CREATETABLEinventory(idBIGINTAUTO_INCREMENTPRIMARYKEY,product_idBIGINTNOTNULL,countINTNOTNULLDEFAULT0);INSERTINTOinventory(product_id,count)VALUES(1,100);CREATETABLEundo_log(-- 同上);

account-service数据库

CREATEDATABASEaccount_db;USEaccount_db;CREATETABLEaccount(idBIGINTAUTO_INCREMENTPRIMARYKEY,user_idBIGINTNOTNULL,moneyDECIMAL(10,2)NOTNULLDEFAULT0.00);INSERTINTOaccount(user_id,money)VALUES(1,1000.00);CREATETABLEundo_log(-- 同上);

4. 代码实现

4.1 父项目POM.xml

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.18</version></parent><properties><spring-cloud.version>2021.0.8</spring-cloud.version><seata.version>1.7.1</seata.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>${seata.version}</version></dependency></dependencies></dependencyManagement>

4.2 订单服务实现

4.2.1 POM.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies>
4.2.2 application.yml
server:port:8081spring:application:name:order-servicedatasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTCusername:rootpassword:rootcloud:nacos:discovery:server-addr:localhost:8848openfeign:client:config:default:connectTimeout:5000readTimeout:5000jpa:hibernate:ddl-auto:updateshow-sql:trueseata:enabled:truetx-service-group:my_test_tx_groupregistry:type:nacosnacos:application:seata-serverserver-addr:localhost:8848group:SEATA_GROUPconfig:type:nacosnacos:server-addr:localhost:8848group:SEATA_GROUPdata-source-proxy-mode:AT
4.2.3 订单实体
@Entity@Table(name="orders")@DatapublicclassOrder{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;privateLonguserId;privateLongproductId;privateIntegercount;privateBigDecimalmoney;privateIntegerstatus=0;}
4.2.4 Feign客户端
@FeignClient(name="inventory-service")publicinterfaceInventoryFeignClient{@PostMapping("/inventory/deduct")Stringdeduct(@RequestParamLongproductId,@RequestParamIntegercount);}@FeignClient(name="account-service")publicinterfaceAccountFeignClient{@PostMapping("/account/deduct")Stringdeduct(@RequestParamLonguserId,@RequestParamBigDecimalmoney);}
4.2.5 订单服务实现(核心)
@ServicepublicclassOrderService{@AutowiredprivateOrderRepositoryorderRepository;@AutowiredprivateInventoryFeignClientinventoryFeignClient;@AutowiredprivateAccountFeignClientaccountFeignClient;/** * 创建订单,Seata AT模式分布式事务 * @GlobalTransactional:标记为全局事务 */@GlobalTransactional(name="create-order-tx",rollbackFor=Exception.class)publicStringcreateOrder(Orderorder){System.out.println("开始创建订单...");// 1. 创建订单orderRepository.save(order);System.out.println("订单创建成功:"+order.getId());// 2. 扣减库存StringinventoryResult=inventoryFeignClient.deduct(order.getProductId(),order.getCount());System.out.println("库存扣减结果:"+inventoryResult);// 3. 扣减账户余额BigDecimaltotalMoney=order.getMoney().multiply(newBigDecimal(order.getCount()));StringaccountResult=accountFeignClient.deduct(order.getUserId(),totalMoney);System.out.println("账户扣减结果:"+accountResult);// 模拟异常,测试回滚// if (true) throw new RuntimeException("模拟异常,测试回滚");System.out.println("订单创建完成!");return"订单创建成功:"+order.getId();}}
4.2.6 订单控制器
@RestController@RequestMapping("/order")publicclassOrderController{@AutowiredprivateOrderServiceorderService;@PostMapping("/create")publicStringcreateOrder(@RequestBodyOrderorder){returnorderService.createOrder(order);}}

4.3 库存服务实现

4.3.1 application.yml
server:port:8082spring:application:name:inventory-servicedatasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3306/inventory_db?useSSL=false&serverTimezone=UTCusername:rootpassword:rootcloud:nacos:discovery:server-addr:localhost:8848jpa:hibernate:ddl-auto:updateshow-sql:trueseata:enabled:truetx-service-group:my_test_tx_group# 其他配置同order-service
4.3.2 库存服务实现
@ServicepublicclassInventoryService{@AutowiredprivateInventoryRepositoryinventoryRepository;/** * 扣减库存,分支事务 */@Transactional(rollbackFor=Exception.class)publicStringdeduct(LongproductId,Integercount){System.out.println("开始扣减库存...");// 查询库存Inventoryinventory=inventoryRepository.findByProductId(productId);if(inventory==null){thrownewRuntimeException("库存不存在");}// 检查库存是否充足if(inventory.getCount()<count){thrownewRuntimeException("库存不足");}// 扣减库存inventory.setCount(inventory.getCount()-count);inventoryRepository.save(inventory);System.out.println("库存扣减成功:"+productId);return"库存扣减成功";}}

4.4 账户服务实现

4.4.1 application.yml
server:port:8083spring:application:name:account-servicedatasource:driver-class-name:com.mysql.cj.jdbc.Driverurl:jdbc:mysql://localhost:3306/account_db?useSSL=false&serverTimezone=UTCusername:rootpassword:rootcloud:nacos:discovery:server-addr:localhost:8848jpa:hibernate:ddl-auto:updateshow-sql:trueseata:enabled:truetx-service-group:my_test_tx_group# 其他配置同order-service
4.4.2 账户服务实现
@ServicepublicclassAccountService{@AutowiredprivateAccountRepositoryaccountRepository;/** * 扣减账户余额,分支事务 */@Transactional(rollbackFor=Exception.class)publicStringdeduct(LonguserId,BigDecimalmoney){System.out.println("开始扣减账户余额...");// 查询账户Accountaccount=accountRepository.findByUserId(userId);if(account==null){thrownewRuntimeException("账户不存在");}// 检查余额是否充足if(account.getMoney().compareTo(money)<0){thrownewRuntimeException("余额不足");}// 扣减余额account.setMoney(account.getMoney().subtract(money));accountRepository.save(account);System.out.println("账户余额扣减成功:"+userId);return"账户余额扣减成功";}}

5. Seata Server部署

5.1 下载Seata Server

从Seata官网下载Seata Server 1.7.1:https://github.com/seata/seata/releases

5.2 配置Seata Server

5.2.1 registry.conf
registry { type = "nacos" nacos { application = "seata-server" serverAddr = "localhost:8848" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" dataId = "seataServer.properties" } }
5.2.2 seataServer.properties

在Nacos中创建配置:

  • Data ID: seataServer.properties
  • Group: SEATA_GROUP
  • 配置内容:
    store.mode=db store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC store.db.user=root store.db.password=root store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.lockTable=lock_table store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000

5.3 启动Seata Server

# 解压后进入bin目录./seata-server.sh-h127.0.0.1-p8091-mdb

6. 启动Nacos

# 解压后进入bin目录./startup.sh-mstandalone

7. 测试Seata AT模式

7.1 启动服务

  1. 启动Nacos
  2. 启动Seata Server
  3. 启动order-service
  4. 启动inventory-service
  5. 启动account-service

7.2 测试正常流程

7.2.1 发送请求
curl-XPOST-H"Content-Type: application/json"-d'{"userId":1,"productId":1,"count":10,"money":100.00}'http://localhost:8081/order/create
7.2.2 预期结果
  • 订单服务:创建订单成功
  • 库存服务:库存从100减到90
  • 账户服务:账户余额从1000减到0
  • 全局事务提交成功

7.3 测试回滚流程

7.3.1 修改订单服务代码

OrderService.createOrder方法中添加异常:

// 模拟异常,测试回滚if(true)thrownewRuntimeException("模拟异常,测试回滚");
7.3.2 发送请求

同7.2.1

7.3.3 预期结果
  • 订单服务:订单创建后回滚
  • 库存服务:库存从100减到90后回滚到100
  • 账户服务:账户余额从1000减到0后回滚到1000
  • 全局事务回滚成功

8. Seata AT模式核心机制

8.1 SQL执行与Undo Log生成

8.1.1 执行SQL
UPDATEinventorySETcount=count-10WHEREproduct_id=1;
8.1.2 生成Undo Log

Seata会自动拦截SQL,生成Undo Log并写入数据库:

INSERTINTOundo_log(branch_id,xid,context,rollback_info,log_status,log_created,log_modified)VALUES(123456,'xid-123','serializable','{"beforeImage":{"count":100},"afterImage":{"count":90}}',1,NOW(),NOW());

8.2 事务回滚机制

8.2.1 回滚流程
  1. TC通知RM执行回滚
  2. RM读取Undo Log
  3. RM执行回滚SQL:UPDATE inventory SET count = 100 WHERE product_id = 1 AND count = 90;
  4. RM删除Undo Log
8.2.2 回滚条件
  • 乐观锁机制:回滚SQL包含当前值条件,确保数据未被其他事务修改
  • 幂等性:多次回滚操作结果一致

9. 监控与管理

9.1 Seata控制台

Seata提供了Web控制台,用于监控全局事务状态:

  • 访问地址:http://localhost:7091
  • 默认用户名/密码:seata/seata

9.2 核心监控指标

  • 全局事务数量:当前活跃的全局事务数量
  • 分支事务数量:当前活跃的分支事务数量
  • 事务成功率:全局事务成功率
  • 事务平均耗时:全局事务平均执行时间
  • 回滚事务数量:回滚的全局事务数量

10. 最佳实践

10.1 配置优化

  • 数据库连接池:增大连接池大小,支持更多并发事务
  • Undo Log清理:定期清理过期的Undo Log,避免磁盘空间占用过大
  • 超时设置:合理设置全局事务超时时间,避免事务长时间占用资源

10.2 开发注意事项

  • @GlobalTransactional:仅在事务发起方添加,分支事务无需添加
  • 本地事务:分支事务仍需添加@Transactional注解
  • 幂等性:业务逻辑需考虑幂等性,避免重复执行
  • 异常处理:合理处理异常,确保事务能正确回滚
  • 避免长事务:全局事务应尽可能短,减少资源占用

10.3 部署建议

  • Seata Server高可用:部署多个Seata Server实例,避免单点故障
  • 数据库分离:Seata数据库与业务数据库分离,提高性能
  • 网络优化:确保Seata Server与业务服务网络通畅,减少网络延迟

11. 常见问题与解决方案

11.1 问题:全局事务未生效

解决方案

  • 检查@GlobalTransactional注解是否添加
  • 检查XID是否正确传递(通过Feign拦截器)
  • 检查Seata配置是否正确

11.2 问题:事务回滚失败

解决方案

  • 检查Undo Log表是否正确创建
  • 检查数据库权限是否足够
  • 检查乐观锁条件是否满足(数据是否被其他事务修改)

11.3 问题:性能问题

解决方案

  • 优化SQL,减少执行时间
  • 增大Seata Server的线程池大小
  • 考虑拆分大事务为多个小事务

12. 总结

Seata AT模式是一种非侵入式的分布式事务解决方案,通过自动生成Undo Log和两阶段提交,实现了分布式事务的自动管理。其核心优势包括:

  • 非侵入式:无需修改业务代码,只需添加@GlobalTransactional注解
  • 自动回滚:自动生成Undo Log,支持事务回滚
  • 高性能:异步提交,减少锁竞争
  • 易于集成:与Spring Cloud、Dubbo等框架无缝集成
  • 可靠性高:支持事务回滚,确保数据一致性

通过本实例,您可以深入理解Seata AT模式的工作原理和实现细节,并在实际项目中应用Seata AT模式解决分布式事务问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/26 21:48:29

pikachu-RCE,越权,目录遍历

RCE 漏洞成因&#xff1a;RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 远程系统命令执行 一般出现这种漏洞&#xff0c;是因为应用系统从设计上需要给用户提供指定的…

作者头像 李华
网站建设 2026/7/20 23:37:40

Linux crontab定时任务:Miniconda-Python脚本自动化执行

Linux crontab定时任务&#xff1a;Miniconda-Python脚本自动化执行 在高校实验室的服务器机房里&#xff0c;一位研究生正为每周重复的手动模型训练感到疲惫——每次都要登录、激活环境、运行脚本、检查日志。而隔壁团队却早已实现“躺平式科研”&#xff1a;每天凌晨自动完成…

作者头像 李华
网站建设 2026/7/26 18:55:36

Token长度与成本关系分析:合理规划API调用

Token长度与成本关系分析&#xff1a;合理规划API调用 在AI应用日益普及的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;已经深度嵌入到内容生成、智能客服、代码辅助等多个业务场景中。然而&#xff0c;随着调用量的增长&#xff0c;许多团队开始发现——账单的增长…

作者头像 李华
网站建设 2026/7/15 3:02:14

Conda info查看Miniconda环境详细信息

Conda info查看Miniconda环境详细信息 在如今的 AI 实验室、数据科学团队或云原生开发环境中&#xff0c;你是否遇到过这样的场景&#xff1a;同事说“代码在我机器上能跑”&#xff0c;但你拉下项目后却报错一堆依赖冲突&#xff1f;又或者&#xff0c;在服务器上部署模型训练…

作者头像 李华
网站建设 2026/7/17 9:07:31

开源贡献流程:向Miniconda-Python3.9镜像提PR

开源贡献流程&#xff1a;向Miniconda-Python3.9镜像提PR 在 AI 工程项目日益复杂的今天&#xff0c;一个常见的痛点浮出水面&#xff1a;不同团队成员使用不同的操作系统和 Python 环境&#xff0c;导致“在我机器上能跑”的尴尬局面频发。更别提当某个依赖包升级后&#xff0…

作者头像 李华
网站建设 2026/7/15 1:49:14

HTML表格动态生成:Pandas+Miniconda输出网页报告

HTML表格动态生成&#xff1a;PandasMiniconda输出网页报告 在数据驱动决策的时代&#xff0c;一份清晰、专业且可复现的分析报告往往比原始数据本身更具价值。然而&#xff0c;许多团队仍在用“复制粘贴”方式将 Pandas 的 DataFrame 手动导入 Word 或 PPT&#xff0c;不仅效…

作者头像 李华