Seata分布式事务框架:核心原理、四大模式与Spring Cloud整合实战 📅 2026/8/26 9:15:47 1. 项目概述为什么我们需要Seata这样的分布式事务框架如果你正在开发一个微服务架构的系统比如一个电商应用那么“订单与库存分布式事务”这个问题你大概率已经遇到了或者即将遇到。想象一个最简单的场景用户下单购买一件商品这个操作会触发两个服务订单服务创建订单和库存服务扣减库存。在单体应用里这两个操作在同一个数据库事务里要么都成功要么都失败这很简单。但拆分成微服务后订单和库存各有自己的数据库你无法再用一个本地数据库事务来保证它们的一致性。这就是分布式事务的典型困境。我见过不少团队在初期选择“逃避”这个问题或者用一些“土办法”来应对。比如先扣库存再创建订单如果订单创建失败再调用一个接口把库存加回去。这听起来可行但在高并发下库存回加可能失败或者网络抖动导致回加请求没发出去最终数据就对不上了。另一种常见做法是依赖消息队列做“最终一致性”这确实是一种成熟的方案也就是热词里提到的“最大努力通知”模式但它对业务有侵入性需要设计补偿逻辑开发复杂度不低并且无法做到实时一致性对于强一致性的金融、交易核心场景并不完全适用。正是在这种背景下像Seata这样的分布式事务中间件才显得尤为重要。它把分布式事务的复杂性封装起来提供了一套相对透明、统一的解决方案让开发者可以像使用本地事务一样通过一个注解GlobalTransactional来管理跨服务的业务操作。Seata 这个名字是Simple Extensible Autonomous Transaction Architecture的缩写它的目标就是让分布式事务的使用变得简单。网上有些资料或旧版本里提到的“Seta”通常是笔误或旧称官方和社区统一使用的都是Seata。所以这个“持续学习中”的项目本质上是一次对分布式事务核心解决方案的深度探索与实践。它不是为了学习而学习而是为了解决微服务架构下数据一致性这个无法绕开的、实实在在的工程难题。无论你是刚开始接触微服务的新手还是正在为线上数据不一致问题头疼的资深开发者理解并掌握 Seata 的原理与实战都是一项极具价值的投资。2. Seata 核心架构与事务模式深度解析要用好 Seata绝不能停留在“加个注解就能用”的层面。你必须理解它内部是如何工作的这样才能在出现问题时进行排查也能根据业务场景选择最合适的事务模式。Seata 的架构主要包含三个核心组件理解了它们就理解了 Seata 的骨架。2.1 核心组件TC、TM、RM 各司其职Seata 定义了三个角色这几乎是所有分布式事务理论如 XA、TCC的抽象模型Seata 对其进行了具体实现。事务协调者 (Transaction Coordinator TC)这是 Seata 的服务端需要独立部署。你可以把它想象成分布式事务的“大脑”或“裁判”。它维护全局事务的运行状态负责协调并驱动全局事务的提交或回滚。我们常说的 Seata-Server 就是指 TC。它的高可用至关重要生产环境通常需要集群部署。事务管理器 (Transaction Manager TM)这是集成在客户端应用中的一部分。它定义了全局事务的边界负责开启一个全局事务并最终向 TC 发起全局提交或全局回滚的决议。在代码中那个标注了GlobalTransactional的方法其所在的客户端就是 TM 的角色。资源管理器 (Resource Manager RM)同样集成在客户端应用中。它负责管理分支事务即每个微服务自己的本地事务相关的资源向 TC 注册分支事务、报告分支事务状态并驱动分支事务的提交和回滚。简单说每个参与分布式事务的微服务其内部与数据库交互的部分就是 RM。它们三者的交互流程以一个下单扣库存的场景为例TM订单服务向TC申请开启一个全局事务TC 生成一个唯一的全局事务 IDXID并贯穿整个调用链。订单服务的 RM执行本地“创建订单”事务并向 TC 注册一个分支事务将 undo_log回滚日志写入订单数据库。订单服务调用库存服务并将 XID 通过请求头如Seata-Xid传递过去。库存服务的 RM执行本地“扣减库存”事务同样向 TC 注册一个分支事务并将 undo_log 写入库存数据库。当所有业务逻辑执行完毕TM根据结果向TC发起全局提交或回滚。TC根据决议驱动所有相关的RM完成最终的数据提交或回滚具体机制因事务模式而异。2.2 四大事务模式AT、TCC、Saga、XA 该如何选择这是 Seata 最核心的部分也是你选择方案时的决策依据。热词里提到的“分布式事务四种方案”在 Seata 的语境下主要就是指这四种模式。2.2.1 AT 模式默认且最常用ATAuto Transaction模式是 Seata 的招牌也是默认模式。它对业务代码几乎无侵入你只需要加一个GlobalTransactional注解。原理基于支持本地 ACID 事务的关系型数据库如 MySQL。其核心是“两阶段提交”的优化版。一阶段业务数据和回滚日志undo_log在同一个本地事务中提交。这个 undo_log 记录了数据修改前后的镜像用于回滚。二阶段提交TC 通知各 RM 异步删除对应的 undo_log 即可因为一阶段已经提交了本地事务数据已经持久化。所以提交非常快。回滚TC 通知各 RM 根据 undo_log 生成反向 SQL 并执行完成数据回滚然后删除 undo_log。优点使用简单性能好一阶段就提交了本地事务锁持有时间短。缺点全局行锁AT 模式在一阶段会获取全局锁如果两个全局事务修改同一行数据后发起的事务会尝试获取前一个事务的全局锁如果等待超时则回滚。这保证了隔离性但也可能引发死锁或热点数据并发问题。SQL 支持限制并非所有 SQL 都支持例如某些数据库的 DDL、触发器、存储过程可能无法被完美地记录 undo_log。适用场景绝大多数需要对数据库进行增删改的常规业务场景。如果你的业务不涉及特别复杂的 SQL 或极高的热点数据并发AT 模式是首选。实操心得使用 AT 模式务必确保每个参与事务的数据库中都创建了undo_log表Seata 提供了建表语句。这是 AT 模式能工作的基石。另外要关注seata.client.tm.degrade-check等降级配置防止 TC 集群故障导致整个系统不可用。2.2.2 TCC 模式强一致性与高性能的平衡TCCTry-Confirm-Cancel模式是一种侵入性较强但更灵活的模式。它不依赖数据库的本地事务而是通过业务代码来实现两阶段。原理要求你为每个分支事务设计三个业务方法Try尝试执行。完成所有业务检查并预留好必要的业务资源例如冻结库存而不是直接扣减预扣金额而不是直接扣款。Confirm确认执行。在 Try 成功的基础上真正执行业务操作例如将冻结的库存扣减掉。Confirm 必须保证幂等性。Cancel取消执行。释放 Try 阶段预留的业务资源例如解冻库存。Cancel 也必须保证幂等性。优点完全由业务逻辑控制锁粒度可以做到很高的并发性能。不依赖数据库事务可以适用于非关系型数据库、外部系统调用等场景。缺点代码侵入性极强每个参与事务的服务都需要改造实现三个接口开发、测试和维护成本高。业务设计复杂需要仔细设计资源预留逻辑确保 Confirm/Cancel 的幂等性、空回滚Try未执行Cancel却执行了、防悬挂Cancel 比 Try 先执行等问题。适用场景对性能、一致性要求极高且业务逻辑适合做资源预留的场景。例如金融行业的资金交易、库存量少且并发极高的秒杀场景。2.2.3 Saga 模式长事务的最终一致性解决方案Saga 模式适用于业务流程长、参与者多的场景。它也是最终一致性模型但通过补偿机制来保证。原理将一个长事务拆分成多个连续的本地事务子事务。每个子事务都有对应的补偿动作。Saga 有两种执行方式TCC 式命令式需要像 TCC 一样为每个服务编写正向操作和补偿操作。状态机式声明式通过状态机定义整个业务流程和每个节点的补偿逻辑配置更清晰但需要学习状态机 DSL。优点一阶段就提交本地事务无锁性能好。特别适合长时间运行的业务流程。缺点不保证隔离性可能出现“脏读”A事务未完成B事务看到了A的中间状态。需要业务层自己处理或接受这种弱一致性。适用场景电商的订单履约流程创建订单 - 扣库存 - 发货 - 结算、酒店机票预订、银行开户等包含多个步骤的长链路业务。2.2.4 XA 模式数据库原生支持XA 模式是分布式事务的“古典”标准依赖数据库本身提供的 XA 协议。原理也是两阶段提交但 RM 就是数据库本身。TM 通过 TC 调用数据库的 XA 接口。在一阶段数据库执行 SQL 但不提交等待 TC 指令二阶段TC 通知所有数据库一起提交或回滚。优点强一致性业务无侵入和 AT 类似。缺点数据锁定时间长一阶段不提交资源数据行锁会一直持有到二阶段对并发性能影响很大。依赖数据库厂商实现不同数据库的 XA 实现和支持度有差异。适用场景对一致性要求极高且可以接受较低并发、较短事务时间的场景。现在通常被 AT 模式所取代。选择建议新手或大多数业务优先考虑AT 模式简单够用。高并发秒杀、金融交易评估TCC 模式用复杂度换取性能和强一致性。跨系统、长流程业务考虑Saga 模式。遗留系统或数据库特性要求考虑XA 模式。3. 从零开始Seata 环境搭建与 Spring Cloud 整合实战理论懂了接下来就是动手。这里我以最常用的AT 模式整合Spring Cloud Alibaba Nacos Seata为例带你走一遍完整的搭建流程。这个组合也是目前微服务生态里的“明星套餐”。3.1 部署 Seata Server (TC)TC 需要独立部署。你可以选择从 Seata 官网 下载发行版或者用 Docker 部署。步骤 1下载与解压去 GitHub Release 页面下载最新稳定版的 Seata Server。解压后目录结构如下seata/ ├── bin/ ├── conf/ │ ├── file.conf │ └── registry.conf └── lib/步骤 2配置注册中心和存储模式Seata TC 需要将自己的地址注册到某个注册中心如 Nacos以便客户端TM/RM发现它。同时它需要存储全局事务、分支事务等元数据。修改conf/registry.conf将 type 改为nacos并配置你的 Nacos 服务器地址。registry { type nacos nacos { application seata-server serverAddr 127.0.0.1:8848 group SEATA_GROUP namespace cluster default username password } } config { type nacos nacos { serverAddr 127.0.0.1:8848 namespace group SEATA_GROUP username password dataId seataServer.properties } }这里我们把配置也放在了 Nacos实现动态配置。修改存储模式关键Seata TC 默认将数据存在本地文件单机测试可以生产环境必须用数据库。在 Nacos 上创建seataServer.properties配置文件内容如下# 事务日志存储模式db代表数据库 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?useUnicodetruecharacterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseSSLfalse store.db.userroot store.db.passwordyour_password # 初始连接数 store.db.minConn5 # 最大连接数 store.db.maxConn30同时在你的 MySQL 中创建seata数据库并执行 Seata 发行版script/server/db目录下的mysql.sql脚本创建所需的全局事务表、分支事务表等。注意事项生产环境务必使用db模式并做好数据库的高可用。file模式仅用于演示数据无法持久化TC 重启后事务状态会丢失。步骤 3启动 Seata Server进入bin目录执行启动脚本。Linux/Mac:sh seata-server.shWindows:双击 seata-server.bat(热词里有人搜“windows安装seata”这就是方法)启动成功后在 Nacos 控制台的服务列表里应该能看到一个名为seata-server的服务。3.2 客户端TM/RM整合 Spring Cloud 微服务假设我们有两个 Spring Boot 微服务order-service和storage-service。步骤 1添加依赖在每个服务的pom.xml中引入 Spring Cloud Alibaba 和 Seata 的依赖。!-- Spring Cloud Alibaba 依赖管理 -- dependencyManagement dependencies dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2022.0.0.0/version !-- 使用与你Spring Boot版本兼容的版本 -- typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- Nacos 服务发现 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency !-- Seata -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-seata/artifactId /dependency !-- 其他依赖如 Web, MyBatis等 -- /dependencies步骤 2配置客户端在application.yml中配置 Seata 和 Nacos。spring: application: name: order-service # 服务名 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 datasource: url: jdbc:mysql://localhost:3306/order_db?useSSLfalse username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver # Seata 配置 seata: application-id: ${spring.application.name} tx-service-group: my_tx_group # 事务组需与TC配置对应 enable-auto-data-source-proxy: true # 开启数据源自动代理这是AT模式的关键 config: type: nacos nacos: server-addr: 127.0.0.1:8848 group: SEATA_GROUP ># 事务组映射到TC集群 service.vgroupMapping.my_tx_groupdefault步骤 3创建 undo_log 表在order_db和storage_db中分别执行 Seata 提供的undo_log表建表语句在script/client/at/db目录下。步骤 4编写业务代码与全局事务注解在订单服务的下单方法上添加GlobalTransactional注解。Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private StorageService storageService; // Feign客户端用于调用库存服务 Override GlobalTransactional(name create-order, rollbackFor Exception.class) // 开启全局事务 public void createOrder(Order order) { // 1. 本地事务创建订单 orderMapper.insert(order); // 2. 远程调用扣减库存 (这是一个分支事务) storageService.deduct(order.getProductId(), order.getCount()); // 模拟异常测试回滚 // int i 1/0; } }库存服务的deduct方法就是一个普通的业务方法不需要特殊注解Seata 的 RM 会自动代理其数据源将其纳入全局事务。步骤 5启动与测试依次启动 Nacos、Seata-Server、库存服务、订单服务。通过 API 工具调用下单接口。正常情况下订单和库存会同时成功。如果你在订单服务方法里取消注释那行模拟异常的代码再次调用会发现订单和库存的操作都被回滚了数据保持一致。4. 生产环境进阶高可用配置、性能调优与问题排查把 Seata 跑起来只是第一步要上生产还有一堆坑要踩。这里分享一些实战经验。4.1 高可用部署与配置单点 TC 是致命的。生产环境必须集群化。TC 集群部署部署多个 Seata-Server 实例通过 Nginx 等负载均衡器对外提供统一地址或者让客户端直连 Nacos利用 Nacos 的负载均衡机制。在registry.conf中为每个 TC 配置相同的cluster “default”或根据机房划分客户端会从同集群内选择实例。数据库高可用TC 的存储数据库store.modedb必须使用主从复制、集群等高可用方案避免数据库单点故障导致所有事务状态丢失。客户端重试与降级配置seata.client.tm.degrade-checktrue和seata.client.tm.degrade-check-allow-times当 TC 集群不可用时可以降级为不使用全局事务保证核心业务流程不中断但失去了一致性保证需业务权衡。事务分组tx-service-group这是一个重要的概念。你可以将不同的业务模块划分到不同的事务组映射到不同的 TC 集群实现资源隔离和故障隔离。4.2 性能调优要点undo_log 表优化undo_log表会频繁插入和删除提交后删除。确保该表有合适的索引默认建表语句已包含并定期清理已提交很久的日志Seata 有内置的异步任务但也可自定义清理策略。全局锁竞争AT 模式的全局锁是性能瓶颈之一。对于更新极其频繁的热点数据如秒杀库存考虑使用 TCC 模式或者改用 Redis 分布式锁最终一致性方案。在 Seata 配置中可以调整全局锁获取的重试次数和间隔 (client.rm.lock.retryTimes,client.rm.lock.retryInterval)。TC 端调优调整 TC 的线程池参数server.undo.logSaveDays,server.maxCommitRetryTimeout等根据事务吞吐量进行调整。监控 TC 的 JVM 内存和 GC 情况。RPC 框架选择Seata 的 TC 与 RM/TM 之间通过 Netty 进行 RPC 通信。确保网络延迟低、带宽足。在云原生环境下可以考虑使用 gRPC 等更高效的协议Seata 已支持。4.3 常见问题排查实录在实际运维中你会遇到各种各样的问题。这里记录几个典型的排查场景。问题 1全局事务不回滚现象在GlobalTransactional方法内抛了异常但数据没有回滚。排查检查异常类型GlobalTransactional默认只回滚RuntimeException和Error。如果你抛的是Exception需要显式指定rollbackFor Exception.class。检查切面顺序如果项目中还有其他的 AOP如 Spring 的事务管理Transactional可能会存在切面顺序问题导致 Seata 的全局事务切面未生效。可以通过Order注解调整顺序确保 Seata 的切面在最外层。检查 XID 传递在微服务调用链中XID 是通过请求头如Seata-Xid传递的。如果你使用了自定义的 HTTP 客户端或过滤器可能会丢失这个头。确保你的 Feign、RestTemplate 等客户端配置了 Seata 的上下文拦截器。查看 TC 日志登录 Seata Server 控制台或查看日志确认该全局事务是否被正确注册以及回滚指令是否下发。问题 2脏写数据覆盖现象两个全局事务同时更新同一行数据后提交的事务覆盖了前一个事务的更新。原因与解决这是 AT 模式隔离级别默认读未提交导致的问题。Seata 的 AT 模式默认是读未提交因为一阶段提交后数据就可见了。要解决脏写需要开启全局锁默认已开启。确保你的业务 SQL 是UPDATE ... WHERE ...形式Seata 才能通过前置镜像和后置镜像来检测脏写并通过全局锁阻止。对于高并发更新如前所述考虑 TCC 或业务设计上避免热点。问题 3undo_log 表数据堆积现象undo_log表越来越大影响性能。排查检查异步删除Seata 在全局提交后会异步删除 undo_log。检查是否有大量事务长时间未完成悬挂导致日志无法删除。可以通过 TC 控制台查看全局事务状态。配置日志保留时间在 TC 配置中设置server.undo.logSaveDays默认 7 天TC 会定期清理超过保留时间的日志。手动清理脚本对于极端情况可以编写定时任务手动删除status 1已提交且log_created时间过早的记录。问题 4Seata 与 MyBatis-Plus 等ORM框架的兼容性现象使用 MyBatis-Plus 的saveOrUpdate等方法时回滚可能不生效。解决Seata 的 AT 模式依赖于解析执行的 SQL 来生成 undo_log。一些 ORM 框架的复杂方法可能生成非标准的 SQL或者批量操作可能导致 Seata 的 SQL 解析器无法正确工作。稳妥的做法是在全局事务方法内尽量使用简单的、明确的insert、update、delete语句。如果必须使用复杂操作需要进行充分的测试。问题排查工具箱Seata TC 控制台通过serverIp:7091访问可以查看全局事务、分支事务的实时状态是首要的排查工具。客户端日志将io.seata包的日志级别设置为DEBUG或TRACE可以打印出 XID 传递、分支注册、锁获取等详细过程。数据库日志查看undo_log表的数据看是否有对应的记录生成和删除。分布式链路追踪结合 SkyWalking、Zipkin 等工具将 XID 作为 TraceId 的一部分进行传递和展示可以可视化整个全局事务的调用链。最后我想说的是分布式事务没有银弹。Seata 的 AT 模式以其低侵入性成为了一个优秀的“默认选择”但它并非万能。技术选型的核心在于权衡在一致性、可用性、性能和复杂度之间找到最适合你当前业务阶段的平衡点。理解每种模式的原理和代价才能在问题出现时从容应对甚至提前规避。持续学习 Seata不仅仅是学习一个工具更是学习在分布式系统下如何驾驭数据一致性这一复杂命题的思维方式。