恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

基于Seata AT模式的微服务分布式事务完整落地实践

  • 首页
  • 资讯中心
  • /
  • 基于Seata AT模式的微服务分布式事务完整落地实践

相关资讯

MySQL MVCC原理详解:从隐藏列、undo log到ReadView的完整机制 2026/10/19 14:29:44
GEO优化公司比稿选型避坑笔记:从流量缺口到供应商筛选 2026/10/19 14:29:44
基于MATLAB/Simulink的4机10节点系统暂态稳定仿真与CCT扫描 2026/10/19 14:29:44

最新资讯

生产级RAG评测实战:可归因、可操作的端到端评测体系
单周期CPU设计:数据通路与Verilog实现避坑指南
东芝3525ac打印错误与脱机故障排查指南
火车排号问题详解:用栈模拟判断合法出栈序列
为什么Medusa推理加速是端侧AI的关键?Phi-3-mini-128k-instruct_rai_1.8.0_medusa_1.0_npu投机解码原理详解
轻量级开源代码审查工具open-code-review:从零到一实践指南

今日推荐

OpenCowork 实测:支持本地文件、飞书机器人的 Windows AI 助手(只需配置 Token)
GPT-6 Sol 与 Luna 怎么选?Chat、Work、Codex 版本差异与 TaoToken 接入实测
openclaw.net 集成 ElBruno.MempalaceNet 记忆系统:把 MCP 配置改到 TaoToken 的完整实践

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

基于Seata AT模式的微服务分布式事务完整落地实践

发布时间:2026/10/19 14:29:44
基于Seata AT模式的微服务分布式事务完整落地实践 只要做微服务早晚会遇到分布式事务的坑。单体时代一个本地事务搞定订单和库存拆分成两个服务、两个库之后同样一个下单接口订单写进去了库存却扣失败了账就对不上了。我的做法是用 Seata 的 AT 模式配合 Spring Cloud Alibaba 和 Nacos 做注册发现与配置管理把这套分布式事务方案完整落地。这篇以我实际搭过的订单服务加库存服务为例从 Seata Server 部署、微服务客户端整合到跨库联调验证和踩坑排查一步一步讲清楚。需要说明的是文中的配置和版本组合是基于我项目里验证过的常用组合你也可以按自己的环境微调但核心链路是通用的。1. 先搞清楚AT模式在幕后做了什么1.1 三个角色和一次全局事务的完整旅程Seata 把一次分布式事务拆成了三个角色TC事务协调器就是独立的 Seata Server、TM事务管理器一般是发起全局事务的业务服务、RM资源管理器也就是真正操作数据库的服务。一次下单流程走下来大致是这样订单服务作为 TM方法上加了GlobalTransactional注解它先向 TC 注册一个全局事务拿到一个全局唯一的 XID。随后订单服务通过 Feign 调库存服务XID 会跟着调用链传递过去库存服务作为 RM 把自己执行的本地事务注册成这个全局事务的分支事务。全部本地事务先各自执行成功TM 再给 TC 发起全局提交如果中间任何一步抛出异常TM 发起全局回滚TC 通知所有分支事务回滚。这个链路里最容易被忽略的是 XID 的传递。Spring Cloud Alibaba 的 Seata 集成包给 Feign 加了拦截器会自动把 XID 塞进请求头所以普通 Feign 调用不用你管。但如果项目里用了 RestTemplate、异步线程池或者消息队列XID 不会自动传你得手动把 XID 放进请求头或消息体里否则后面的服务根本不知道自己参与了全局事务出了问题也不会跟着回滚。1.2 为什么AT模式对业务代码的侵入这么小AT 模式最吸引我的一点是业务 SQL 基本不用改。它的思路是在每个参与事务的业务数据库里建一张undo_log表本地事务执行前RM 会先记录数据的镜像beforeImage执行 SQL 后再记录一份 afterImage然后本地提交。如果后面需要回滚TC 会通知 RMRM 通过 undo_log 里存的前后镜像把数据恢复到执行前。全局提交时则把 undo_log 记录删掉就行。这套机制比 TCC 省事太多了。TCC 要你为每个操作都写 Try、Confirm、Cancel 三个方法还得考虑幂等和悬挂工作量明显大。AT 模式本质上是用额外的日志记录换来了低侵入适合大多数订单、库存、账户这类业务场景。1.3 AT/TCC/SAGA怎么选我自己的选择标准是这样的业务方法相对同步、链路不算太长、对一致性要求高优先 AT。接口必须对性能敏感AT 模式在数据竞争激烈时会频繁出现全局锁等待这时候 TCC 反而更可控。长事务、跨系统强异构场景比如涉及到外部接口或者异步补偿链路SAGA 更合适。Seata 也支持 TCC 和 Saga但如果你是从零开始整合我建议先用 AT 模式跑通链路别一上来就上 TCC。2. 部署Seata Server前的关键准备2.1 Nacos上需要提前准备好的配置项Seata Server 要挂到 Nacos 上做注册同时它的全局配置也可以托管到 Nacos。我用的是 Seata 1.4.2 版本配合 Nacos 1.4.x比较稳。启动之前先在 Nacos 控制台新建一个配置Data ID 我习惯叫seataServer.propertiesGroup 用SEATA_GROUP。内容大致如下store.modedb store.db.datasourcedruid store.db.dbTypemysql store.db.driverClassNamecom.mysql.cj.jdbc.Driver store.db.urljdbc:mysql://127.0.0.1:3306/seata_server?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai store.db.userroot store.db.password你的数据库密码 service.vgroupMapping.my_test_tx_groupdefault service.disableGlobalTransactionfalse client.rm.lock.retryInterval10 client.rm.lock.retryTimes30 client.rm.lock.retryPolicyBranchRollbackOnConflicttrue这里service.vgroupMapping.my_test_tx_groupdefault特别关键它把客户端配置的事务分组映射到 Seata Server 集群里的一个虚拟组default。如果这行配置缺失客户端启动时大概率会出现找不到服务的问题。很多教程会忽略我刚开始也在这里卡了半天。2.2 Seata Server的配置落点与DB模式初始化Seata Server 有两个核心配置文件1.4.2 版本是registry.conf和file.conf。前者管注册中心和配置中心的连接方式后者管事务存储等本地配置。registry.conf里我把 registry 和 config 都指向 Nacosregistry { type nacos nacos { application seata-server serverAddr 127.0.0.1:8848 group SEATA_GROUP namespace username nacos password nacos } } config { type nacos nacos { serverAddr 127.0.0.1:8848 namespace group SEATA_GROUP dataId seataServer.properties } }注意如果config.type设置为 nacosSeata Server 就会去 Nacos 拉取seataServer.properties里的配置本地file.conf里的 store 配置就不生效了。我建议直接把配置都托管到 Nacos这样后续 Server 端调参数不用再改配置文件重启方便很多。2.3 启动TC并确认注册成功Seata Server 的 DB 模式需要三张表global_table、branch_table、lock_table它们建在一个独立的库里我一般叫seata_server。脚本在 Seata 解压目录的script/server/db/mysql.sql里直接执行即可。启动步骤cd /opt/seata/bin sh seata-server.sh -p 8091 -h 127.0.0.1 -m db启动后看日志确认没有报错然后去 Nacos 控制台的服务列表里找seata-server这个服务看到实例在线就算成功。这里有个小经验Server 的-h参数一定要能跟客户端互通的主机 IP不要写localhost否则跨服务器调用时客户端会拿着127.0.0.1去连 TC连接必然失败。3. 微服务客户端整合的完整步骤3.1 依赖引入与版本对齐先看 Maven 依赖。我的项目是 Spring Cloud Alibaba 的在pom.xml里引入dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-seata/artifactId /dependency这个依赖会传递引入 Seata 的 client 端。要特别留意版本冲突spring-cloud-starter-alibaba-seata 里内置的 Seata 版本必须与你的 Seata Server 一致或者至少兼容。否则客户端可能用一套协议Server 用另一套连上了也无法正常工作。我用的版本组合是 Spring Cloud Alibaba 2.2.5.RELEASE Seata 1.4.2。你可以根据自己项目的 Spring Cloud 版本选择一个经过验证的对应关系不要盲目升级 Seata 到最新版稳定优先。3.2 客户端配置事务分组与注册中心指向客户端的application.yml里需要告诉 Seata 三件事当前服务叫什么、事务分组是什么、注册中心在哪里。spring: application: name: order-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 alibaba: seata: tx-service-group: my_test_tx_group seata: enabled: true registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 group: SEATA_GROUP namespace: username: nacos password: nacos config: type: nacos nacos: server-addr: 127.0.0.1:8848 group: SEATA_GROUP namespace: username: nacos password: nacos这里最核心的是tx-service-group它必须和 Seata Server 端配置的service.vgroupMapping.my_test_tx_group前半部分对应。如果服务端映射的是order_tx_group客户端写my_test_tx_group那 TM 发起全局事务时就找不到TC直接报错。库存服务也要写同样的配置只是spring.application.name改成自己的服务名。两个服务的事务分组保持一致它们才能参与同一个全局事务。3.3 数据源代理与undo_log表落地AT 模式要记录前后镜像就必须让 Seata 接管你的数据源。Seata 会通过DataSourceProxy代理我们原有的 DataSource在执行 SQL 时采集数据变化、写 undo_log。我的做法是写一个数据源配置类Configuration public class DataSourceProxyConfig { Bean Primary ConfigurationProperties(prefix spring.datasource) public DataSource dataSource() { return new DruidDataSource(); } Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } }这里踩过一个大坑如果项目里有多数据源或者 MyBatis 的SqlSessionFactory绑定到了原始数据源那 Seata 根本不会拦截到 SQL。必须让 MyBatis、JdbcTemplate 等最终使用的都是DataSourceProxy包装后的数据源。如果项目里有多个数据源 Bean记得把代理数据源标上Primary。另外undo_log表必须建在每个参与全局事务的业务数据库里。脚本在 Seata 解压目录的script/client/mysql/undo_log.sql。漏建这张表的话本地事务提交的时候可能没立刻报错等全局回滚时就会发现没有日志可回滚或者直接抛异常。建议在做整合检查时把订单库、库存库的 undo_log 表都查询一遍确认存在。表结构核心字段包括branch_id、xid、rollback_info和log_status。回滚信息是序列化后的二进制数据Seata 用它来做数据恢复。3.4 在入口方法上开启全局事务事务开启的位置很有讲究。全局事务注解要加在整个分布式事务的入口方法上也就是能代表一个完整业务闭环的方法。订单场景的入口在订单服务里Service public class OrderService { private final OrderMapper orderMapper; private final StorageFeignClient storageFeignClient; public OrderService(OrderMapper orderMapper, StorageFeignClient storageFeignClient) { this.orderMapper orderMapper; this.storageFeignClient storageFeignClient; } GlobalTransactional(rollbackFor Exception.class, timeoutMills 30000) public void createOrder(OrderDTO orderDTO) { Order order new Order(); order.setUserId(orderDTO.getUserId()); order.setCommodityCode(orderDTO.getCommodityCode()); order.setCount(orderDTO.getCount()); order.setAmount(orderDTO.getAmount()); orderMapper.insert(order); StorageDTO storageDTO new StorageDTO(); storageDTO.setCommodityCode(orderDTO.getCommodityCode()); storageDTO.setCount(orderDTO.getCount()); storageFeignClient.deduct(storageDTO); } }注意GlobalTransactional并不替代本地Transactional。如果入口方法里既有本服务的数据库操作又有远程调用最好在方法内部用TransactionTemplate或者加上Transactional控制本地事务的边界。否则本地 SQL 可能在全局事务还没结束时就已经被提交一旦后续远程调用失败本地事务已经落库回滚只能靠 undo_log性能会差一些。4. 模拟跨库下单场景验证全局事务4.1 场景搭建订单库与库存库我验证用的拓扑很简单一个 MySQL 实例建两个库分别是order_db和storage_db。order-service连order_db管理订单表。storage-service连storage_db管理库存表。两个服务都注册到 Nacos。Seata Server 独立部署使用seata_server库存事务状态。这个拓扑基本还原了实际项目里的分库效果。核心验证目标只有一个库存扣减失败时订单是否会被自动回滚。4.2 验证路径A正常流程全部提交先跑正常流程确保链路本身是通的。调用下单接口如果订单插入成功库存也扣减成功再分别查两个库的数据能看到订单记录和扣减后的库存。同时去seata_server库查global_table会看到一条记录status 为提交完成状态。branch_table里能看到两个分支事务对应订单服务和库存服务。undo_log表在正常提交后应该被清理说明二阶段提交成功。第一次跑通的过程其实比我想的要顺利前提是配置没出错。如果你在验证时发现订单和库存都成功了但 global_table 里没有记录说明GlobalTransactional根本没生效。最常见原因就是事务分组映射不对或者注解加错了方法。4.3 验证路径B库存扣减失败触发全链路回滚接下来模拟失败场景。我在库存服务里主动抛一个运行时异常Transactional(rollbackFor Exception.class) public void deduct(String commodityCode, int count) { Storage storage storageMapper.selectByCommodityCode(commodityCode); if (storage null) { throw new RuntimeException(库存记录不存在); } storageMapper.deduct(commodityCode, count); throw new RuntimeException(模拟扣减失败); }注意库存服务这里用本地Transactional保证自己的先回滚然后 Seata 的全局回滚会再对订单服务做一次补偿回滚。整个调用链结束后查order_db的订单表发现订单记录已经不存在说明全局事务回滚生效了。这里我给一个小建议验证回滚时先在日志里确认 XID 有从订单服务传到库存服务。Seata 的日志里会打印类似xid的信息如果没有传递两个服务各自为政库存失败不会影响订单那问题就出在请求头传递上需要重点检查 Feign 拦截器或者自定义调用链。4.4 从undo_log和TC日志看二阶段行为回滚成功后再去看库存库的undo_log会发现回滚分支的日志记录还在但log_status已经被标记成已回滚状态订单库里的 undo_log 则被清理掉了。这其实是 AT 模式标准的二阶段表现。如果回滚过程中有问题日志里会出现rollback error或者branch rollback failed之类的信息。我后面要讲的踩坑部分很大比例就是围绕这个环节展开的。5. 整合过程中高频踩坑的完整排查链路5.1 客户端一直报can not connect to service这是我遇到的最多的一个问题。现象是启动订单服务后一调用带GlobalTransactional的接口就抛can not connect to services-server这种异常或者提示找不到服务。按这个顺序排查先确认 Seata Server 是否已经注册到 Nacos注册名是不是seata-server。客户端配置里的registry.nacos.application必须能对应上。再看客户端的tx-service-group和 Server 端service.vgroupMapping.xxx是否一致。不一致的话客户端不知道用哪个虚拟组找 TC。最后确认网络。Server 启动时-h参数绑定的 IP客户端必须能访问到。如果 Server 在另一台机器-h 127.0.0.1会让客户端往自己的网卡回环地址连必然失败。这三步按顺序走基本能解决大部分连接问题。5.2 全局事务没生效回滚了个寂寞另一个高频问题是库存抛异常了订单还是没回滚。这个问题的排查链路我建议这样走查订单方法上GlobalTransactional是否真的加载到了。如果方法被同类内部调用Spring 的this调用会绕开代理注解失效。这一点非常阴险我一开始也怀疑框架有问题最后发现是自己调用姿势的问题。看日志里有没有 XID 的创建与传递记录。TM 发起全局事务日志会显示Begin new global transaction之类的信息。如果根本没这行说明注解没生效。检查两个服务的事务分组是否一致。如果订单服务用的是group A库存服务用的是group B那它们虽然都能连接 TC但根本不认为自己在同一个全局事务里。确认 SQL 操作有没有真正走DataSourceProxy。可以在 undo_log 里查一下如果一阶段执行后 undo_log 没数据说明数据源没代理上。5.3 回滚失败的根因beforeImage与afterImage校验回滚失败比较隐蔽表现是日志里有xid、有分支注册但回滚时报镜像校验失败。AT 模式回滚时会用 undo_log 里的 afterImage 和当前数据库中的实际数据做对比。如果当前数据已经被其他事务改了和 afterImage 不一致Seata 不敢强制恢复只能报警并让该分支进入人工处理状态。这个问题的根源通常是全局事务执行期间另一个请求把同一行数据改了。避免手段是尽量缩短全局事务的执行时间把远程调用和重IO操作放进事务之前完成减少持有全局锁和本地锁的时间。也可以在查询/更新数据时考虑在相关事务边界使用SELECT FOR UPDATE或GlobalLock来隔离。不过这不是银弹根本上还是要让事务链路短而快。5.4 版本兼容矩阵与常见坑位最后说版本。根据我实际验证过的组合Spring Cloud Alibaba 和 Seata 的常见对应关系可以做个参考Spring Cloud Alibaba内置 Seata 版本我测试过的稳定性2.2.5.RELEASE1.4.2稳定2021.0.1.01.4.2稳定2021.0.5.01.5.2基本稳定2021.0.6.01.6.x需要自行验证请注意官方发布页的版本对应才是最终依据上表只是我个人的验证记录。版本不对齐的时候最常见的坑是注册成功但全局事务无响应或者二阶段回调时客户端根本处理不了。升级 Seata 时建议连同 Seata Server 和所有客户端一起升不要只升一边。另外如果你用了 Druid 连接池注意DataSourceProxy包装的是 DruidDataSource。如果 Druid 的监控页或部分插件拿的是原始数据源会出现监控数据正常但 Seata 不生效的情况所以要把数据源 Bean 的注入关系梳理清楚。我把数据源代理统一放在一个配置类里明确标注Primary之后排查起来非常省事。最后再分享一个实操习惯。每整合完一个新的 Seata 项目我会先跑一遍最小场景两个服务、两张业务表、一次带远程调用的全局事务故意在第二个服务里抛一个异常看第一个服务是否回滚。这个冒烟测试通过了再往复杂业务上加。别急着一步到位分布式事务这种业务越往后越难排查先把底座打牢后面扩展才不慌。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号