SpringCloudAlibaba Seata分布式事务 AT模式

发布时间:2026/10/23 13:06:19

SpringCloudAlibaba Seata分布式事务 AT模式 还有一篇文章是SpringCloudAlibaba Seata分布式事务那篇是14年总结的等找到工作抽时间整合到这块儿这篇文章的内容都DeepSeek提供的不得不说它的总结非常清楚明白所以这次面试的话可以利用DeepSeek重新整理一篇概念核心角色TC (Transaction Coordinator) - 事务协调器独立部署的调度中心负责维护全局事务和分支事务的状态驱动提交或回滚。TM (Transaction Manager) - 事务管理器嵌入在应用中的“事务发起者”用GlobalTransactional定义全局事务边界。RM (Resource Manager) - 资源管理器管理每个分支事务即本地数据库操作负责向TC注册、上报状态并执行提交/回滚指令。其中TC为单独部署的Server服务端TM和RM为嵌入到应用中的 Client 客户端TC就是事务管理器RM就是每个数据库连接或者说本地事务TM就是事务的入口或者说事务的发起方当然它本身也是一个RM执行流程TM请求TC开启一个全局事务TC会生成一个XID作为该全局事务的编号XID会在微服务的调用链路中传播保证将多个微服务的子事务关联在一起RM请求TC将本地事务注册为全局事务的分支事务通过全局事务的XID进行关联TM请求TC告诉XID对应的全局事务是进行提交还是回滚TC驱动RM将XID对应的自己的本地事务进行提交还是回滚每个微服务都需要有一个undo_log表用于自动补偿例如一个事务已经提交了但这时发生了异常此时该事务已经无法回滚了。在提交事务之前会查询一下要执行事务涉及到的表数据并记录在undo_log表中如果发生了上面的情况则将数据自动改回去而不需要我们敲代码来执行这个操作。二阶段提交精髓在于“一阶段提交二阶段补偿”第一阶段PrepareTM向TC申请开启全局事务生成全局唯一的XID并传播到各个微服务。RM执行业务SQL同时Seata的数据源代理会拦截SQL记录数据修改前后的镜像before-image和after-image作为回滚日志undo_log保存在当前数据库中。在本地事务提交前RM会向TC申请一个全局锁针对修改的数据行。拿到全局锁后本地事务提交业务数据更新生效本地锁被释放。这个“一阶段就提交并释放本地锁”是AT模式高性能的关键。第二阶段Commit / Rollback全局提交如果所有分支都成功TC通知各RM异步删除undo_log并释放全局锁非常快。全局回滚如果任一分支失败TC通知所有RM进行回滚。RM根据XID找到对应的undo_log用before-image的旧数据生成反向SQL如补偿更新将数据恢复然后删除日志并释放全局锁。关键机制 Undo Log与全局锁Undo Log回滚日志这是AT模式实现自动回滚的核心。它在每个参与事务的数据库中必须创建一张undo_log表记录数据更新前后的快照。通过解析这些快照Seata能在需要时自动生成补偿SQL对业务代码零侵入。隔离性实现Seata AT模式的隔离性基于“全局锁”实现写隔离核心是“先抢全局锁再提交本地事务”。不同全局事务要修改同一行数据时必须先获得该行的全局锁。这有效防止了脏写但引入全局锁竞争也是其性能瓶颈之一。读隔离默认是读未提交因为一阶段已提交。如果需要“读已提交”的强隔离需在查询SQL中使用SELECT … FOR UPDATE此时Seata会代理该语句去申请全局锁从而读到已提交的数据测试seata:enabled:trueapplication-id:awu-springboot-code-1# 事务分组名称你可以随便起但必须和下面 mapping 的 key 对应tx-service-group:default_tx_groupregistry:type:nacosnacos:server-addr:192.168.239.3:8848username:nacospassword:nacos# 关键桥梁告诉客户端你的 default_tx_group 去找 Nacos 里名为 default 的集群Seata 2.0 服务端默认就是注册到 defaultservice:vgroup-mapping:default_tx_group:defaultpackagecom.awu.springboot.code1.controller;importcom.awu.springboot.code1.feign.OrderFeignClient;importio.seata.spring.annotation.GlobalTransactional;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;RestControllerRequestMapping(/distributed-tx-test)publicclassDistributedTxTestController{AutowiredprivateOrderFeignClientorderFeignClient;// ⚠️ 全局事务注解加在发起入口GetMapping(/user-place-order)GlobalTransactionalpublicStringuserPlaceOrder(){System.out.println( 开始Code1用户下单入口 );// 模拟 用户下单主表入库System.out.println(【成功】Code1 用户下单记录写入数据库成功);System.out.println(--- Code1 准备调用 Code2 生成订单 ---);returnorderFeignClient.createOrder();}}packagecom.awu.springboot.code2.controller;importcom.awu.springboot.code2.feign.StockFeignClient;importcom.awu.springboot.code2.mapper.User;importcom.awu.springboot.code2.mapper.UserMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;RestControllerRequestMapping(/distributed-tx-test)publicclassDistributedTxTestController{AutowiredprivateStockFeignClientstockFeignClient;AutowiredUserMapperuserMapper;GetMapping(/create-order)publicStringcreateOrder(){System.out.println( 1. 进入 Code2订单服务 );// 模拟 生成订单写入数据库System.out.println(【成功】Code2 生成订单成功(假装写入了订单表));User张三newUser(1L,张三,55);userMapper.updateById(张三);System.out.println( 1. 进入 Code2订单服务 );System.out.println(当前 XID: io.seata.core.context.RootContext.getXID());System.out.println(--- Code2 准备调用 Code3 扣减库存 ---);returnstockFeignClient.decreaseStock();}}packagecom.awu.springboot.code3.controller;importcom.alibaba.fastjson.JSON;importcom.awu.springboot.code3.mapper.User;importcom.awu.springboot.code3.mapper.UserMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;RestControllerRequestMapping(/distributed-tx-test)publicclassDistributedTxTestController{// Autowired// private StockService stockService; // 你的库存ServiceAutowiredUserMapperuserMapper;GetMapping(/decrease-stock)publicStringdecreaseStock(){System.out.println( 2. 进入 Code3扣减库存服务 );// 模拟 扣减数据库库存System.out.println(【成功】Code3 扣减库存成功(假装从数据库扣了 1 件));// 这里测试脏读会读取到code2服务的脏数据UseruseruserMapper.selectById(1L);System.out.println(JSON.toJSONString(user));// 这里模拟一个异常用于测试全局回滚// User lisi new User(2L, 李四, 22);// userMapper.updateById(lisi);// ⚠️ 重点制造全局回滚异常// System.out.println(--- Code3 检测到异常情况准备回滚 ---);// throw new RuntimeException(【模拟报错】Code3 库存扣减服务发生故障触发全局回滚);return扣减成功;}}问题同一个全局事务内的脏读问题code1远程调用code2code2修改了当前行数据code2远程调用code3code3读取了当前行数据他们在同一个全局事务内那么code3是读到了code2修改前的还是修改后的数据呢是修改后的数据因为同一个事务内修改后的数据是可见的Seata 的设计是同一个全局事务内的分支事务对该事务自己持有的锁是“可重入”的。Code2和Code3属于同一个全局事务Code2加的锁Code3可以直接通过不会阻塞等待。如果Code2和Code3在同一个全局事务中Code3读到Code2的未提交数据是符合 Seata 设计预期的。这并不算真正的“脏读”而是同一事务内的数据可见性。解决方案先调用code3后调用code2不同全局事务的脏读处理for update GlobalLock Transactional这可以解决不同事务之间的脏读问题它会去获取全局锁我们可以设置重试时间让它直到获取锁成功为止才读到数据这样就避免了脏读的问题。seata:client:rm:lock:# 全局锁重试间隔毫秒默认 10msretry-interval:10# 全局锁重试次数默认 30 次retry-times:300000# 如果设为 true则重试次数无限retry-policy-branch-rollback-on-conflict:falseenabled:trueapplication-id:awu-springboot-code-3# 事务分组名称你可以随便起但必须和下面 mapping 的 key 对应tx-service-group:default_tx_groupregistry:type:nacosnacos:server-addr:192.168.239.3:8848username:nacospassword:nacos# 关键桥梁告诉客户端你的 default_tx_group 去找 Nacos 里名为 default 的集群Seata 2.0 服务端默认就是注册到 defaultservice:vgroup-mapping:default_tx_group:defaultGetMapping(“/dirty-read”)public String dirtyRead() {User user userService.selectByIdForUpdateDirtyRead(1L);log.info(“测试不同事务”);return JSON.toJSONString(user);}OverrideGlobalLockTransactionalpublic User selectByIdForUpdateDirtyRead(Long id) {// 测试用for update避免脏数据这里seata会去拿全局锁User user userMapper.selectByIdForUpdate(1L);System.out.println(JSON.toJSONString(user));return user;}Select(“SELECT * FROM user WHERE id #{id} FOR UPDATE”)User selectByIdForUpdate(Long id);AT 模式脏写问题 ⭐非常常问二阶段提交 / 回滚消息丢失怎么办幂等怎么保证全局锁超时时间在绝望的时候朱重八不止一次地祈求上天从道教地太上老君到佛教的如来佛祖只要他能知道的名字都祈求到了祈祷的唯一内容只是希望与父母在一起生活下去有口饭吃。明朝那些事儿当年明月
延伸阅读

更多相关文章

2026/10/22 1:35:22

长春燃气/电热水器维修上门服务-欧米到家持证师傅正规检修|深度排查不出热水漏水忽冷忽热等故障

冬季洗澡水温异常、热水器漏水、不出热水、运行异响、水温忽冷忽热,是长春家庭热水器最常见的故障。很多长春用户网上找维修师傅,容易遭遇小病大修、隐形收费、劣质配件等问题。市面上多数维修内容内容零散、实用性差,无法适配长春老小区水压…

2026/10/20 14:12:54

Chrome 偷吃 4GB 硬盘?把本地 AI 模型删干净并禁止它再下载

先说结论 最近有些机器上的 Google Chrome 会在用户没有明确感知的情况下,下载一份约 4GB 的本地生成式 AI 模型数据。它通常不是病毒,也不是缓存异常,而是 Chrome 为本地 AI 功能准备的模型文件。只删除目录只能临时释放空间,真正…

2026/10/23 13:01:19

ThinkPHP教学管理系统实战:环境搭建、核心模块与避坑指南

简介:这是一套基于ThinkPHP框架搭建的简单教学管理系统源码,面向PHP初学者、Web开发入门者及需要课程设计参考的学生,帮助理解MVC架构下的教务管理实现。压缩包共2000个文件,约11.04MB,以1105个php源码为核心&#xff…

2026/10/23 13:01:19

一体化管理工具:多站点登录态维持与任务调度实战

1. 从零散工具到统一面板:为什么需要一体化管理1.1 一个真实场景引出的痛点我接触过不少玩私有影音库的朋友,几乎每个人都经历过同一个阶段:硬盘里攒了几十上百个资源站账号,浏览器书签栏塞得满满当当,每次想找一部片子…

2026/10/23 13:01:19

IE Tab Multi(Enhance):Chrome中嵌入真实IE内核的兼容方案

简介:IE Tab Multi (Enhance)_12.2.12.1-0.zip 是一款专为Windows平台Chrome浏览器设计的IE兼容性增强扩展离线安装包,面向企业内网用户、政务/金融类系统运维人员及需频繁访问ActiveX控件网页的开发者。它在Chrome中无缝嵌入IE渲染引擎,避免…

2026/10/23 13:01:19

MATLAB形态学与边缘检测实现药片缺陷检测的工程化落地

简介:本资源面向图像处理初学者与工业视觉检测方向的开发者,围绕药片缺陷与有无检测这一典型场景,提供一套基于MATLAB的完整实现方案。包内共15个文件,以7张jpg测试图像、5个m脚本文件、2个doc与1个docx说明文档为主,压…

2026/10/23 12:56:18

自定义AI智能体:打造后端开发专属搭子实战指南

1. 为什么我要把AI助手改造成专属后端开发搭子刚接触AI编程助手那会儿,我跟大多数人一样,把它当成一个高级点的代码补全工具——敲几个字符,它帮我补全一行;写个函数名,它帮我生成个大概框架。用了一段时间之后我发现&…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/23 0:02:26

Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战

说实话,这几年接触了不少想入门FPGA的朋友,如果你问哪个项目能一口气把Verilog的核心基本功串起来,我通常会推荐电子密码锁。这个项目看着不起眼,却把矩阵键盘扫描、按键消抖、状态机设计、动态显示刷新、外设控制这些数字逻辑里的…

2026/10/23 0:02:26

数据服务监控运维实战:从指标体系到告警治理与故障排查

周末凌晨两点,手机震了。第一反应是“数据服务挂了”。打开电脑看了下,果然,某张核心报表的数据产出时间已经超过SLA两小时,下游三个业务方连着发来问询。这种场景,做过大数据运维的人都不会陌生。数据服务的监控与运维…

2026/10/23 0:02:26

FreeRTOS任务通知实战详解:内核原理、API与避坑指南

裸机编程切到RTOS之后,任务之间的通信和同步永远是躲不开的坎。前面聊过队列、信号量、互斥锁、事件组,这次把FreeRTOS任务通知(Task Notification)单独拎出来讲透。原因很简单:任务通知是FreeRTOS里最容易用错、也最容…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑