分布式事务:Seata AT 模式与 Saga 模式怎么选?
分布式事务:Seata AT 模式与 Saga 模式怎么选?
Section titled “分布式事务:Seata AT 模式与 Saga 模式怎么选?”只要你走上了微服务架构这条不归路,你就一定会撞上这座大山:分布式事务。
假设你有一个经典的电商场景:用户下单。 在微服务下,这涉及到了三个独立的服务,以及它们各自独立的物理数据库:
- 订单服务(创建订单)
- 库存服务(扣减库存)
- 账户服务(扣减余额)
如果在扣减余额时报错了,订单服务怎么回滚?库存怎么加回来? 在过去,大家使用 2PC(两阶段提交)或者 XA 协议。但这玩意极其沉重,它在整个事务期间会一直锁住数据库里的那几行数据,导致系统并发量低得令人发指。
在 2026 年,国内处理分布式事务的绝对霸主是阿里的开源中间件 Seata。面对复杂的业务,我们通常在它提供的 AT 模式 和 Saga 模式 中做出抉择。
1. 敏捷开发的首选:Seata AT 模式
Section titled “1. 敏捷开发的首选:Seata AT 模式”AT (Auto Transaction) 模式是 Seata 最受欢迎的模式,因为它是对业务代码几乎零侵入的。你只需要像以前一样在代码上加一个 @GlobalTransactional 注解即可。
它的底层魔术是什么?
Section titled “它的底层魔术是什么?”AT 模式的本质是:两阶段提交的优化版 + 数据库的 Undo Log(回滚日志)。
- 第一阶段(本地提交并释放锁):
- 订单服务插入数据前,Seata 代理(Proxy)会拦截 SQL,把插入前后的数据快照记录下来,存到数据库一张专门的
undo_log表中。 - 然后立刻提交本地数据库事务,并释放本地数据库锁!(这是它比传统 XA 性能高的根本原因)。
- 订单服务插入数据前,Seata 代理(Proxy)会拦截 SQL,把插入前后的数据快照记录下来,存到数据库一张专门的
- 第二阶段(决议):
- 如果所有服务都成功了,Seata TC(事务协调器)下发指令,各个服务异步把
undo_log里的垃圾记录删了即可。 - 如果账户服务报错了,Seata TC 下发回滚指令。订单服务去查自己的
undo_log表,生成反向的 SQL(比如之前是INSERT,现在就执行DELETE),把数据恢复成原来的样子。
- 如果所有服务都成功了,Seata TC(事务协调器)下发指令,各个服务异步把
AT 模式的致命伤:脏写问题
Section titled “AT 模式的致命伤:脏写问题”因为第一阶段立刻释放了本地锁,这就导致在整个分布式事务还没结束时,这行数据实际上已经暴露给外界了(读未提交级别)。
如果此时有一个没有接入 Seata 的老系统,跑过来把这条订单数据改了。
等到我们要回滚时,Seata 会发现:现在的数据库里的数据,和我在 undo_log 里记录的数据不一样!这就发生了**“脏写”**,Seata 会拒绝回滚,直接抛出异常,需要人工介入修数据。
适用场景:公司内部的微服务,大家都连着 Seata,且对性能要求不是极其变态(因为还是要写 undo_log 和获取全局锁)。
2. 长事务与跨公司的利器:Saga 模式
Section titled “2. 长事务与跨公司的利器:Saga 模式”如果你的业务流程特别长(比如订机票、订酒店、再租车,可能要跑几分钟),或者你需要调用第三方银行的接口(你不可能让银行的数据库去记什么 undo_log)。
这时候 AT 模式彻底抓瞎。你必须使用 Saga 模式。
什么是 Saga?
Section titled “什么是 Saga?”Saga 没有什么框架底层的魔术,它是纯粹的业务层面的补偿。 它把一个巨大的分布式事务,拆成了 N 个独立的本地小事务,并且要求你必须为每一个小事务人工编写一个“正向操作”和一个“逆向补偿操作”。
- 订单服务执行
createOrder(),补偿操作是cancelOrder()。 - 调用外部航空公司 API
bookTicket(),补偿操作是调用refundTicket()。 - 如果到第三步失败了,Saga 状态机会自动往回执行:先调
refundTicket(),再调cancelOrder()。
Saga 的优势与代价
Section titled “Saga 的优势与代价”- 优势:没有全局锁,并发性能极高!非常适合长事务;而且能够轻松集成异构系统和不可控的外部 API。
- 代价(极高的开发成本):你每写一个业务方法,都必须配对写一个补偿方法。不仅如此,你还要在代码里自己处理“空补偿”(补偿请求先于正向请求到达)、“防悬挂”等极其恶心的边界条件。
3. 架构师的决策建议
Section titled “3. 架构师的决策建议”面对分布式事务,最高级的架构师建议是:能不用,就不用。 能通过领域合并(把订单和库存合并到一个服务里)解决的,就千万别上分布式事务。
如果非要用:
- 优先使用“本地消息表 + 异步 MQ 重试”实现最终一致性。(比如扣完钱发个 MQ 给库存,库存慢慢扣,不要求强一致)。
- 业务闭环且追求开发效率的内部系统:使用 Seata AT 模式。
- 流程极长、涉及跨国/跨公司 API 调用的金融级业务:硬着头皮写 Seata Saga 或者 TCC(Try-Confirm-Cancel)模式,用代码逻辑死死守住资金的安全。