连载中 16/20

分布式事务的微服务落位:Seata 与最终一致

2026-08-30 · 465 阅读 · 0 评论 · 0 赞

拆掉的不只是代码,还有事务

单体时代,下单扣库存是一条 @Transactional 里的事:SQL 都在一个连接里,要么全成、要么全回。服务拆开后,订单在订单服务、库存在库存服务,两个数据库、两个连接——本地事务的边界画不下去了。拆掉的不只是代码,还有事务的物理边界

第一问:能不能不用分布式事务

成熟团队的第一反应不是上方案,而是拆需求:强一致的场景,尽量收敛到一个服务、一个库里(把库存扣减留在订单事务的同库边界的代价与收益,值得反复权衡);能接受最终一致的,改造成事件驱动——订单先落库,发"订单已创建"事件,库存服务异步扣减,失败重试或人工兜底。绝大多数跨服务场景,最终一致就够了,强一致是少数——先绕,绕不开再选方案。

Seata AT:一行注解的代价

AT 模式的卖点是对代码近乎零侵入:方法上加 @GlobalTransactional,框架接管跨服务的回滚。原理两句话:一阶段——各分支本地事务直接提交,同时记录前后镜像到 undo_log;二阶段——全局提交时异步删日志,回滚时按镜像反向补偿。代价藏在细节里:全局锁在事务期间持有,热点数据的并发写会被串行化;分支事务越短越好,长事务会拖死锁表;undo_log 表本身要维护清理。

TCC 与消息最终一致

TCC(Try-Confirm-Cancel)把控制权交给业务:Try 预留资源、Confirm 确认、Cancel 释放——性能好、不依赖锁,但每个参与方要写三个接口加空回滚、幂等、悬挂三大防护,成本最高,只留给资金核心链路。消息最终一致是最常用的折中:本地消息表保证业务与消息的原子性,MQ 投递给下游,消费方幂等——一致性窗口放宽到秒级,换来链路的解耦与削峰能力。

// 本地消息表:业务数据与消息同库同事务,原子性由本地事务保证
@Transactional
public void createOrder(Order order) {
    orderMapper.insert(order);                                 // 业务数据
    outboxMapper.insert(new OutboxMsg("OrderCreated", toJson(order))); // 同事务写消息
}
// 独立投递器扫描 outbox 表发 MQ,成功后标记已发送
// 消费方按 orderId 做幂等,重复消息直接忽略

决策树:按一致性要求与链路形态选

方案一致性侵入性适合场景
AT 模式强(隔离期短)同链路多库,短事务
TCC资金核心,高并发扣减
事务消息/本地消息表最终中低跨域解耦,可异步
Saga/状态机最终长流程,多参与方

一句话总结选型:能绕就绕(改设计),能异步就异步(最终一致),真要强一致再上 AT/TCC。分库分表之后的事务边界问题,在分片场景里还会再变一次形,原理相通。下一篇转向工程实践中最日常也最容易出事的环节:发布策略

503

10 年全栈工程师 · 503咖啡馆主理人

#分布式事务#Seata#AT模式#本地消息表#最终一致

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞