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

Seat分布式事务实战:原理、选型与排错完整指南

  • 首页
  • 资讯中心
  • /
  • Seat分布式事务实战:原理、选型与排错完整指南

相关资讯

广视角技术深度解析:IPS、VA与补偿膜如何决定可视角度 2026/10/21 18:19:14
AI Agent 记忆系统:从上下文窗口到分层记忆架构 2026/10/21 18:14:14
opencode双会话与事件溯源:AI代理执行引擎的核心设计解析 2026/10/21 18:14:14

最新资讯

Sunshine 新手教程:把游戏串流到家里任意一块屏幕
内蒙古科技大学《Applied Catalysis A:General》:60s,850°C焦耳热构筑钴钼碳化物异质界面,助力高效析氧
十分钟从零跑通 RuoYi-Vue-Plus:本地部署与配置完整指南
【译】告知您的模型何时需要进行深度思考
基于SpringBoot+Vue的健身房会员管理系统(设计与实现)
Mur Lant cid 《木兰辞》汉语拼音字母标调实测案例

今日推荐

SAP 屏幕上的金额未必存在数据库表里,从 WB2R_SV 的 Net Amount 看懂运行时计算
具身智能中‘指哪打哪’的视觉语言动作联合建模
AI时代人工翻译技术博客的价值与实践指南

本周热门

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

本月精选

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

Seat分布式事务实战:原理、选型与排错完整指南

发布时间:2026/10/21 18:19:14
Seat分布式事务实战:原理、选型与排错完整指南 最近把 Seat 分布式事务组件完整过了一遍从概念、源码、本地环境到经典的下单扣库存场景总算把整套流程跑通了。这篇笔记既是学习记录也是给想从零开始接触分布式事务、又怕被一堆术语劝退的同学一份实操参考。先说明一下原文标题写的是 Seat我按这个简称来写指的就是那套基于 TC/TM/RM 模型的开源分布式事务中间件学习文档里也一直这么叫。分布式事务这个问题只要你的服务拆过库、拆过服务早晚都会撞上。单体时代一条 SQL 不对就回滚事务边界由数据库帮你画好微服务架构下订单、库存、账户各占一个库业务链路跨越三次远程调用一旦中间某一步失败靠单库事务已经救不回来。Seat 这类中间件的价值就是把这几个库的多个本地事务纳入同一个“全局事务”里统一协调让它们要么全部成功要么全部回滚。下面我把自己的完整学习过程拆开来讲从原理到选型从部署到排错最后再说说源码怎么读、故障怎么演练。1. 为什么需要分布式事务先弄懂这笔账1.1 一个跨库场景引发的“拆账”问题学习分布式事务最好的入口永远是一个真实到不能再真实的例子。我这次用的是最常见的一套链路用户下单订单服务写订单表库存服务扣减商品库存账户服务扣减用户余额。这三个服务对应三个不同的业务库调用顺序是订单服务先落一条订单数据然后远程调用库存服务再远程调用账户服务。假设订单已经写入库存也扣了但账户服务因为余额不足抛了异常整个请求最终返回失败。如果每个服务各自管理本地事务会发生什么结果订单库里有新订单库存库里少了一部分数量账户库里的钱没变。这种“中间状态”一旦出现前端再做一次重试库存继续扣订单再插一条数据就彻底乱了。这个问题的本质是把一张完整的“业务账本”拆到了三个抽屉里。单靠每个抽屉自己锁死没用因为三个抽屉没有一个统一的管理者。生活化一点理解你把一百元现金撕成三瓣分别锁进三个抽屉每个抽屉都说“我这一瓣是安全的”合起来却不是一张能花的钱。分布式事务要解决的核心就是这种跨抽屉的原子性要么三瓣都不动要么拼回一张完整的百元钞。1.2 全局事务模型TC、TM、RM 各管什么Seat 的全局事务模型抽象出了三个角色第一次看的时候会觉得概念多但画一遍流程图就清楚了。TC事务协调器独立部署的服务端负责维护全局事务和分支事务的状态驱动全局提交或全局回滚。你可以把它理解成总调度室。TM事务管理器业务代码里发起全局事务的角色通常是在方法上打一个注解由它向 TC 申请开启全局事务、提交全局事务或回滚全局事务。RM资源管理器管理各分支事务的资源它负责在每个业务库里执行本地事务把本地事务的提交/回滚能力注册到 TC。正常的执行流程是这样的业务方法启动TM 向 TC 申请创建一个全局事务拿到一个全局事务编号 XIDXID 通过远程调用传递到库存服务、账户服务每个服务里的 RM 在执行本地业务 SQL 时会把本地事务注册成这个全局事务的一个分支最后 TM 根据业务结果通知 TC“整体提交”或“整体回滚”TC 再通知所有分支 RM 执行对应动作。实际排查问题时我习惯把 XID 看作快递单号。一个包裹从仓库发出中间经过分拣中心、运输车辆最后到用户手里全程用同一个单号串联。只要这个单号在某一路段断了整个包裹的状态就会对不上——分布式事务里的 XID 断传导致的也是类似的“找不到分支归属”问题。1.3 为什么不自研分布式事务组件很多人看完上面这套模型的第一反应是我也能写一个类似的协调器吧真要动手做的时候会被下面几个问题反复教育。第一是状态存储。全局事务要记录 XID、分支状态、提交回滚信息这些状态不能只放内存否则 TC 一重启就丢存数据库又会引入新的性能瓶颈和依赖。第二是失败恢复。两阶段提交过程中任意一个环节都可能宕机比如 TM 通知 TC 提交后网络断了、某个分支回滚执行到一半服务挂了。要设计超时重试、状态恢复机制工程量和复杂度远超预期。第三是并发控制。多个全局事务同时改同一行数据需要全局锁否则前一个事务还没回滚完后一个事务已经读到脏数据这种隔离问题靠简单加锁很难处理干净。第四是幂等与防悬挂回滚消息重复投递、补偿操作重复执行都需要一层额外的幂等保护。成熟中间件把这些能力都做成了通用基础能力学习阶段没必要重复造轮子。我更建议把精力花在理解它的取舍逻辑上后面用起来才能判断什么场景适合哪种模式。2. 四种事务模式横向对比选型比会写代码更重要2.1 AT 模式最省心的入场券AT 模式是我最初上手的模式也是 Seat 里对业务侵入最小的一种。它的核心思路是通过代理数据源在业务 SQL 执行前后自动记录数据快照并写入一张专门的回滚日志表全局事务回滚时根据这张日志逆向恢复数据。展开讲AT 模式分两个阶段。第一阶段业务方法里执行的 INSERT、UPDATE、DELETE 通过代理数据源在执行前查询原始数据生成“前镜像”执行后再查询变更后的数据生成“后镜像”然后在一个本地事务里把业务 SQL 和回滚日志一起提交。第二阶段TC 通知全局提交时RM 直接删除对应的回滚日志TC 通知全局回滚时RM 根据前镜像数据生成反向 SQL把数据改回去。AT 模式最吸引人的地方是业务代码基本不用改还是正常写 SQL只是把数据源换成 Seat 的代理数据源并且在参与全局事务的业务库中都建好回滚日志表。我在测试环境第一次跑通时几乎是零成本接入。但它也有明显代价提交前数据已经可见为了隔离性每个全局事务在修改数据前要获取全局锁如果热点数据被两个全局事务同时操作后到的一方要等锁锁等待时间一长接口 RT 会飙升。补充一点适用层面的认知。逻辑上 AT 模式对表结构有要求每张被代理表必须有主键否则无法定位回滚数据SQL 类型和复杂度也有限制典型的 INSERT、UPDATE、DELETE 没问题但如果你在事务里混了大量手写 SQL、跨库关联、子查询就必须逐一检查是否在它的支持范围内。2.2 TCC 模式把控制权握在自己手里AT 模式省心但省心的代价是把“如何回滚”这件事交给了框架自动解析。遇到那种对数据正确性极其敏感的场景比如资金账户、积分明细很多人不放心让框架自动生成反向 SQL这时 TCC 模式就是更合适的选项。TCC 分成三个动作Try 阶段做资源检查和预留Confirm 阶段真正执行业务操作Cancel 阶段释放 Try 阶段预留的资源。我一个简单的转账场景为例账户服务先执行 Try检查余额是否足够如果足够就冻结一百元confirm 时把冻结的一百元正式扣掉cancel 时把冻结的一百元解冻回可用余额。写 TCC 最大的难点不是实现三个方法而是要把空回滚、幂等、防悬挂三个问题处理干净。空回滚指的是 Try 阶段还没执行就收到了 Cancel幂等指的是同一条 Cancel 消息重复投递不能重复退款防悬挂指的是 Cancel 先到、Try 后到不能让 Try 的预留动作在 Cancel 之后执行。实际工程中通常靠一张事务控制表记录每个分支事务的状态在里面做状态机流转才能把这些边界情况处理干净。Seat 在 TCC 模式下充当协调者但“怎么冻结、怎么解冻、怎么补偿”的业务逻辑全部由服务自己实现。所以 TCC 定制能力强、性能好但开发成本也最高。这也是为什么很多团队只在资金类和核心链路用 TCC普通业务链路用 AT。2.3 Saga 与 XA长流程和强一致的边界模式Saga 模式适合业务流程长、中间步骤多、且不需要强一致的场景。它不要求所有参与者同时锁定资源而是把一个长事务拆成一系列子事务每个子事务都有对应的补偿操作。比如订单超时自动关闭、多步骤审批、跨组织调度的流程可以用 Saga 编排。某个步骤失败后Saga 会按相反顺序执行前面步骤的补偿操作。Saga 的优点是没有全局锁事务不长时间占用数据库资源适合高并发长流程代价是补偿逻辑要自己写且中间状态对外可见需要业务层接受“最终一致”而非“强一致”。另外 Saga 的事务隔离性几乎为零设计时需要考虑并发读到中间状态的应对策略。XA 模式走的是数据库原生两阶段提交协议由数据库层面保证强一致。它同样要求参与方数据库支持 XA 规范并且在第一阶段就锁住参与的数据锁的粒度更粗等待时间也更长。结果就是代码侵入少但资源开销大。我个人的看法是低并发、强一致、且数据库类型统一的内部系统才会考虑 XA高并发互联网业务基本绕开它。2.4 模式选型速查表把这四种模式放在一起对比会更直观模式业务侵入性一致性并发性能适用场景主要成本AT低改数据源加注解强一致但有全局锁隔离窗口中等热点数据会有锁等待大多数跨库业务场景快速接入回滚日志表、全局锁TCC高需要实现 Try/Confirm/Cancel强一致业务层保证较高资源只在 Try 阶段短暂预留资金类、敏感数据、对回滚有定制需求补偿逻辑、幂等与防悬挂Saga中需要编排补偿流程最终一致高无全局锁长流程、跨系统流程、可接受中间状态补偿实现与状态恢复XA低依赖数据库强一致较低资源锁时长低并发、强一致、数据库统一数据库 XA 支持、性能瓶颈选型没有绝对的标准答案。我的参考顺序是默认 AT遇到性能瓶颈或者严格的资金类场景再换 TCC链路特别长且能接受最终一致时考虑 SagaXA 我只在极少数合规强一致场景才推。3. 实操本地跑通 Seat 跨库下单扣库存3.1 部署 seat-server 与初始化配置理论学习再多不如本地把服务跑起来一次。 Seat 的学习环境不需要很复杂我这次只用了三个 Java 服务和一台 seat-server所有组件都部署在同一台开发机上。seat-server 是独立的协调服务需要三个表来存全局事务状态全局事务表、分支事务表、全局锁表。建表脚本在官方文档里都有但我更建议自己动手建一遍这样对状态数据结构有直观感知。以全局事务表为例核心字段至少包括全局事务 ID、事务状态、应用标识、事务分组、超时时间、创建时间和修改时间分支事务表则记录了每个分支所属的全局事务、分支 ID、资源 ID、分支状态等锁表记录全局锁占用情况用于并发控制。部署步骤我整理成下面的清单准备一个独立的业务数据库专门给 seat-server 存事务状态。把全局表、分支表、锁表脚本执行到这个数据库里。修改 seat-server 配置数据源指向刚建好的库注册中心选择本地单机模式。启动 seat-server 进程注意观察日志输出确认它成功连接了数据库并且端口正常监听。用客户端工具连接端口执行一次简单的命令验证服务在线。这里要说明一下我故意没有引入额外的注册中心组件因为学习阶段单机启动越简单越好。如果一上来就铺一套完整的注册中心、配置中心很容易把学习重点带偏。等理解和掌握核心流程之后再把 server 配置成高可用集群也不迟。3.2 业务端接入全局事务代理数据源、加注解、建 undo_logseat-server 起来之后剩下的工作就是在三个业务服务里接入 Seat。第一位是把数据源切换到 Seat 的代理数据源。AT 模式依赖代理数据源拦截业务 SQL自动生成前后镜像。这一步如果漏掉后续注解再怎么写都不会生效。具体操作是在配置类中构造一个代理数据源替换掉原来的普通数据源所有数据库访问都走这个代理。第二位是在全局事务的入口方法上增加全局事务注解。入口方法一般就是下单接口对应的 service 方法注解里可以指定超时时间。超时时间设置要谨慎我刚开始给的是默认值扣库存链路里还有一个外部接口稍微慢一点就超时回滚后来把超时时间调到了 30 秒才稳定。第三位是给每个参与全局事务的业务库创建回滚日志表。AT 模式要回滚数据靠的就是这张表里的前后镜像数据。回滚日志表必须和业务表放在同一个库里并且要和业务操作在同一个本地事务中提交否则无法保证日志和业务数据的一致。这三个动作做完一个最基础的 AT 模式链路就算搭好了。你甚至不需要改订单、库存、账户三个服务里的任何 SQL只需要保证远程调用链路能正常传递 XID。3.3 下单链路代码示例及关键配置核心入口方法写出来其实很短GlobalTransactional(timeoutMills 30000) public void createOrder(OrderDTO order) { orderDao.insert(order); accountService.deduct(order.getUserId(), order.getAmount()); inventoryService.decrease(order.getSkuId(), order.getCount()); }注意这个注解只加在入口方法上库存服务和账户服务的方法不需要重复加。Seat 的设计是把用户发起的最外层方法作为全局事务的边界里面所有远程调用通过传递的 XID 自动加入到同一个全局事务中。配置文件的常用片段大概是这样的seat: enabled: true application-id: order-service tx-service-group: seat_learning_group registry: type: file client: rm: report-retry-count: 5应用 ID 和事务分组名要对应上 seat-server 里的配置。后一个配置项是 RM 上报分支事务状态的重试次数网络抖动时重试能提高成功率但也不要设置得过大否则会让故障响应变慢。数据源代理的配置看起来是这样Bean public DataSource dataSource(DataSource originalDataSource) { return new DataSourceProxy(originalDataSource); }把普通数据源包装成代理数据源后后续 MyBatis 或者其它持久层框架拿到的都是这个代理对象。这里我踩过一个坑如果工程里存在多个数据源只代理了其中一部分那没代理的数据源里执行的 SQL 不会进入全局事务管理回滚时就会有数据漏掉。3.4 复现一次回滚从日志看全局事务状态环境搭好后我第一次做的是正向测试正常下单确认订单、库存、账户三张表的数据都发生变化全局事务成功提交。第二次做的是回滚测试我在库存服务里故意抛一个运行时异常模拟库存不足。请求返回失败后我重点查三张表的状态。结果是订单表没有新增订单账户表余额没有变化库存表数量也没有减少。表面看起来所有业务数据都恢复了原状但真正让我确认全局事务生效的是日志里几个关键节点。日志会打印出全局事务 ID 的生成过程库存服务抛异常后入口服务的拦截器捕获到异常上报 TC 请求回滚TC 向各个分支 RM 下发回滚命令库存服务这一侧执行反向 SQL 回滚账户服务和订单服务也分别执行回滚最后回滚日志被清理。看到“回滚分支完成”这类日志基本可以认定全局事务链路是通的。这里补充一个数据验证细节回滚完成后除了看业务表数据还要顺手看回滚日志表里还有没有残留数据。正常提交的事务日志会在提交后被清理回滚成功的事务日志也会在回滚后被清理。如果日志表里残留了大量数据说明某个分支的事务状态没有得到最终处理很可能存在异常。4. 事务失效与回滚异常排查这些坑我基本都踩过4.1 全局事务注解不生效这个问题出现的频率最高表现也很迷惑业务方法抛了异常数据却只回滚了本地库远程分支没有回滚。我排查下来原因大致有三种。第一种是注解加错了位置。全局事务注解加在接口方法上而业务实现类里没有加代理对象就无法正确拦截或者你自己 new 了一个业务对象绕过了框架的代理。第二种是方法内部自调用。比如入口方法里调用了同一个类的另一个方法而这个内部方法上加了全局事务注解由于调用没有经过代理对象注解完全不生效。这种情况最隐蔽因为代码看起来没问题。第三种是配置问题。全局事务开关没打开或者事务分组配置不一致导致拦截器没有注册成功。遇到注解不生效优先做三件事确认入口方法在被代理的 Bean 上确认调用链路上没有 this 调用确认配置文件和 seat-server 的事务分组对得上。4.2 undo_log 缺失或回滚失败回滚失败的经典报错大多是“找不到回滚日志”或者“回滚日志为空”。看到这类报错我的第一反应就是去检查对应业务库里有没有建回滚日志表。如果表没建AT 模式的 RM 在执行业务 SQL 时无法记录前后镜像后续回滚自然无从谈起。还有一种情况是表建了但建错库了回滚日志和业务表不在同一个业务库也无法保证本地事务的一致性。另外回滚失败也可能是 SQL 镜像生成不了。比如业务表没有主键或者 SQL 过于复杂代理数据源解析不了。遇到这种问题我的处理原则是能改表的先改表给表补上主键SQL 实在复杂的就考虑换 TCC 模式或者拆分事务。4.3 全局锁等待超时AT 模式下最常见的性能问题就是全局锁等待超时。两个全局事务同时操作同一行数据后一个事务会一直等待前一个事务释放锁如果前一个事务执行时间过长后一个事务就会超时报错。我在测试环境里模拟过这种场景一个事务故意 sleep 十秒另一个事务同时更新同一条记录很快就能看到全局锁冲突的报错。排查时先查锁表看有没有残留锁记录如果锁表是干净的再看是不是业务事务本身太大一个全局事务里塞了太多本地操作导致锁持有时间太长。要优化锁等待一般从几个方向入手尽量缩短全局事务方法体把不必要的远程调用移出去更新条件尽量走索引缩小锁定行数调大锁重试次数和等待时间缓解瞬时冲突。但要记住这些都是缓解手段真正要解决的还是减少大事务。4.4 重复请求和接口幂等有一个认知误区需要纠正Seat 解决的是分布式事务的原子性问题不是接口幂等问题。同一个下单请求被客户端重复提交或者全局事务回滚后前端自动重试这些场景下服务端可能收到两次内容相同的请求如果订单模块没有做幂等校验照样会生成两条订单。Seat 的回滚保证的是单次请求内部的业务一致性跨请求的重复提交需要业务层自己处理。最简单实用的方案是给请求加一个唯一业务编号在入口方法最开始查重或者在订单表上建唯一索引利用数据库约束挡住重复数据。如果后续把全局事务超时时间调大、或者引入消息队列重试机制幂等设计会变得更加重要。4.5 异常场景速查表我把学习过程中踩过的关键问题整理成一张速查表以后遇到类似问题可以直接对号入座。现象可能原因排查动作注解没生效异常后远程分支没回滚代理失效、自调用、配置分组不一致检查入口方法所在 Bean 是否被代理检查事务分组配置回滚时报找不到回滚日志回滚日志表没建或建错库检查业务库是否有 undo_log 表确认与业务表同库回滚日志有残留但数据没变分支状态未终态、回滚方向不对看日志定位是哪个分支未处理查全局事务表状态全局锁等待超时大事务持锁过长、热点行冲突查锁表优化事务边界缩短锁持有时间订单重复创建接口幂等缺失加入唯一业务编号、唯一索引或状态机校验这张表不可能覆盖生产环境的全部问题但学习阶段把它弄明白已经能解决大部分入门者的困惑。5. 学习路线与后续扩展从“会用”到“能改”5.1 源码阅读顺序如果你看完前面的内容把示例跑通了下一步建议去读源码。但这里要提醒一句不要从头到尾按代码文件顺序读否则很快就会被各种类绕晕。我推荐的阅读顺序是先找入口再找状态机。第一步看全局事务注解对应的拦截器搞清楚 TM 是怎么申请全局事务、怎么注册分支的第二步进 TC 端看会话管理模块理解全局事务在内存和数据库中的状态流转第三步看 RM 端回滚日志的生成和回放逻辑这是 AT 模式实现的关键最后再回头看分布式锁的实现细节。源码不需要全部读完把一条主链路跑通比追求覆盖率重要得多。刚开始读源码时可以带一个问题去读一个分支事务从注册到提交中间经过哪些类、哪些状态带着问题读效率要高很多。5.2 用故障注入验证回滚能力只会跑通正常流程不等于真正理解了分布式事务。我后来做了一个本地故障演练把服务关掉、把数据库断开、让接口超时逐个验证 Seat 在这种极端情况下的表现。演练场景至少可以覆盖这几个库存服务在本地事务提交之前抛异常验证全局回滚库存服务在本地事务提交之后、尚未上报分支状态时宕机验证超时通知和分支恢复TC 进程挂掉后客户端全局事务如何处理两个服务同时更新同一行数据验证全局锁的排队与超时。每个场景做完都记录一次业务表的最终数据和日志关键信息。这种演练刚开始会暴露一堆问题比如超时时间设置不合理、数据库连接池配置过小、服务依赖的中间件容错能力不足。但正是这些问题才能让你真正理解分布式事务在生产环境里为什么难。5.3 生产落地先想清楚三件事最后聊一点生产级的思考。分布式事务组件能帮你解决一致性问题但它不是银弹落地前必须要把三件事想清楚。第一件事是所有参与者是否愿意接受基础约束。AT 模式需要在每个参与库建回滚日志表TCC 模式需要为每个接口实现三个方法这些约束在不同团队里推行起来阻力差距很大。第二件事是全局事务里的事务边界和锁冲突。全局事务开启时间越长、涉及的远程调用越多出故障的概率和锁扩散的范围就越大所以进全局事务的代码必须精简。第三件事是监控告警。分布式事务协调器、全局事务成功率和耗时、回滚日志表积压情况都应该有对应的监控指标体系。我在实际使用中发现最容易被低估的其实是业务侧的降级方案。分布式事务不是所有场景都追求强一致如果某条非核心链路允许最终一致那就不应该把它放进强一致的全局事务里。想清楚哪些链路进事务、哪些链路不进事务才是比写代码更难的设计决策。我学 Seat 最大的体会是分布式事务并不是引一个 jar 包就能万事大吉的东西。事务模式选型、数据源代理、XID 传递、回滚日志每个环节都对应一类真实的故障场景。先用 AT 模式把最简单的链路跑通再去对比 TCC 和 Saga 的取舍最后考虑生产落地的可观测性和降级方案。这个顺序能帮你少走弯路。另外再分享一个小技巧每次测试回滚场景时不要只盯着业务表看顺手看一眼回滚日志表和全局事务表的状态很多隐藏问题都是从这里暴露出来的。

关于恒美微站

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

快速链接

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

服务项目

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

联系方式

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

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