连载中 1/18

订单创建了,库存没扣:分布式事务从哪来的

2026-06-14 · 1829 阅读 · 0 评论 · 0 赞

财务对不上的那个月

老王去年主导了单体拆微服务:订单、库存、账户各自独立服务,各自独立的数据库。拆完性能漂亮了一个季度,直到月底财务找上门——订单表里有 37 笔已支付订单,库存系统里查无扣减记录。排查溯源,事故当晚库存服务发布抖动,下单链路里订单事务提交成功了,下一跳调库存服务却超时返回,重试被熔断拦截,库存扣减永远没发生。钱收了,货没锁,这批订单差点超卖。单体时代一个 @Transactional 保平安的日子,彻底结束了。

本地事务为什么鞭长莫及

先复习本地事务的护城河:一个数据库连接里的一组 SQL,要么全提交要么全回滚,ACID 由数据库引擎一手包办。它的边界非常清晰——一个连接、一个库。拆完微服务,一次下单变成三个动作:

begin; 订单库 insert 订单; commit;      ——订单服务自己的连接
http 调用 库存服务 扣减库存               ——另一个进程另一个库
http 调用 账户服务 扣款                   ——又一个进程又一个库

三个动作横跨三个进程、三个数据库连接,任何一个本地事务都只能罩住自己那一小段。订单提交和库存扣减之间隔着一次 HTTP 调用,中间的任何时刻进程崩溃、网络抖动、下游超时,系统就停在半路——这不是代码写得差,是物理规律。

分布式世界的三个现实

把责任推给网络之前,先把三个现实钉在墙上:

现实一:网络不可靠。请求可能丢、响应可能丢、丢多久没人知道。你调库存服务「失败」了,可能库存根本没收到,也可能扣完了响应死在路上。

现实二:失败只是一部分。分布式系统没有「全成或全败」,只有 partial failure——三个动作成了两个,系统既不是完成的也不是清空的,卡在一个语义上说不清的中间态。

现实三:超时是三态,不是两态。本地调用结果非成即败;远程调用多出第三种——不知道成没成。超时后重试,可能造成重复扣款;不重试,可能漏扣。所有分布式事务方案,本质上都在回答同一个问题:第三态怎么办。

直觉解法为什么撑不住

事故后老王的第一版修复很直觉:try-catch 包住三个调用,谁失败就把前面成功的逐个调回去。两周后代码长成这样:订单回滚接口、库存回滚接口、账户回滚接口,回滚链里任何一跳再失败,又得为「回滚的回滚」写逻辑——补偿代码的复杂度指数膨胀,且永远追不上故障组合的数量。更别提回滚接口自己被调了两次怎么办(幂等)、回滚时原服务已经恢复了怎么办(悬挂)——这些坑,后面专篇讲。

直觉解法输在散兵游勇:没有统一的协议管「谁先谁后、失败了谁负责救、救失败怎么办」。业界花了几十年沉淀的,就是把补偿从「各写各的」升级成「有章法的模式」。

方案的谱系:从强一致到最终一致

把市面上的方案按「一致性强度」排成一条谱系,整个系列就住在这条线上:

阵营代表方案核心思路代价
强一致2PC / XA / 3PC先表决再提交,全员点头才生效同步阻塞、可用性差
补偿型TCC / Saga先干,不对就按业务逻辑逆操作业务改造重、写补偿
消息型本地消息表 / 事务消息用可靠消息串起最终一致中间态可见、依赖 MQ
兜底型对账 / 最大努力通知承认会漏,定期找补有时间窗口、人工介入

谱系往左,一致性越强、可用性越差;往右,可用性越好、中间态越明显。老王后来的库存问题最终落在「消息型 + 对账兜底」的组合上——这个选择的推理过程,就是接下来十七篇的内容。

本系列的行军地图

十八篇分四段:第 2 篇打底理论(ACID、CAP、BASE);第 3 到 10 篇逐个拆方案——2PC、TCC 及其三大坑、Saga、本地消息表、事务消息、最大努力通知、幂等设计;第 11 到 15 篇进入框架与实战——Seata 四模式选型、AT 模式原理、热点账户、超时重试、事务边界七宗罪;第 16 到 18 篇收尾——对账系统、踩坑实录、收官地图。

小结

开篇一句话:拆微服务拆掉了本地事务的护城河,网络不可靠、部分失败、超时三态是绕不开的物理现实;分布式事务不是让分布式变回单体,而是用一套套模式管理中间态。先别急着看方案——下一篇把理论地基打平:ACID、CAP、BASE 到底在说什么,以及为什么 BASE 才是互联网架构的宿命。

503

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

#分布式事务#微服务#最终一致#开篇#部分失败

评论 (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 赞