5.4 补偿与Saga画一条退路


5.4 补偿事务与 Saga:不锁死,画一条退路

当"阻塞"本身成为最大的敌人时,补偿型方案给出了另一条路:不追求"全或无"的强原子,而是把长事务拆成一串短事务,允许它们先各自成功,一旦后面某一步失败,就沿原路用"补偿动作"把前面已生效的改回来。本节讲 Saga 的编排方式、TCC 的三段式,以及"补偿"这件事到底由谁、按什么顺序来执行。

学习目标

阅读完本节,你应当能够:

  1. 解释补偿事务的核心思想:每一步都配一个"撤销动作"。
  2. 对比 Saga 的"编排式"与"协同式"两种组织方式并各举适用场景。
  3. 说出 TCC 的 Try、Confirm、Cancel 三步各做了什么。

先给"阻塞"判个死刑

如果你运营的是一个高峰时下几万单的电商,用 2PC 锁着库存等你跨两个服务确认,很可能等来的不是"全或无",而是"全部超时+用户差评"。阻塞在低并发的金融核心库或许能忍,可在互联网的最前线,它是要命的。所以反思任务来了:能不能我干脆不锁,先把业务放行,等它走几步,如果后面发现走错了,再回头把它改回来?—这就引出了补偿型方案。

一、补偿事务的本质:每一步都留 BACK

补偿的思想极朴素:把一个大动作拆成一串小动作,每个小动作成功之后,给它记一个"撤销指南"。当某个小动作失败时,就按"撤销指南"逆序执行前面那些成功的小动作的补偿动作,把系统恢复到开始之前。它放弃的是"强原子"(你可能会短暂看到做了前半段),换到的是"绝不阻塞 + 最后总能改回原样"。对很多"允许短暂中间态"的高并发业务来说,这个交易香甜得过头。

二、Saga 的两种组织方式

编排式 Saga:单独的编排器负责发号:先让 A 服务干活,成功后吹哨让 B 服务干,失败时由编排器按记录一步步指挥前面的服务回滚。好处是全局流程一眼可见、好调试;坏处是编排器又成了中心节点。

协同式 Saga:没有中心编排器,每个服务干完自己的活,就把"下一步该谁干"通过事件/消息告诉下一个服务;失败时也是靠事件链逆序传播去触发各家的补偿。好处是去中心化、扩展性好、更能扛故障;缺点是全局流程散落各处、出了错不好定位

编排式 Saga 的推进与回滚

三、TCC:三个动词把补偿说清楚

TCC(Try-Confirm-Cancel)把每个业务动作拆成三个可调用的动作:Try 先申请资源/预扣(不真生效);Confirm 确认执行(真的落定);Cancel 取消/回滚(释放预扣)。它比 Saga 更"细":Saga 把事务切成"服务级小步骤",TCC 把它切成"资源级的三段动作"。适用上,TCC 更适合那些"要先冻结资源、后续再落定"的典型业务(支付预授权、库存预占)。它最大的代价是侵入性强——每个参与的接口都要为三个动作单独编码,代码量直线上升。

四、补偿由谁、按什么顺序执行

这是补偿型方案最容易踩坑三件事,我逐一提醒:一,补偿必须是"逆序"执行,先成功的最晚被补偿,否则会踩出空指针式的边界——想象一下,先扣了库存再扣钱,如果失败了先补偿库存,钱还在已经扣的状态,你会发现用户被免费发了货。二,补偿动作必须幂等——因为网络会重试,重复执行 Cancel 要能安全地"多做一遍也没事",否则冷启动重试就把补偿做糊了——一次重复补偿可能把已经扣了金额再回滚一次,变成用户拿到了免单。三,补偿的可靠性要高于普通业务——补偿本身也可能失败,得配重试队列、告警,必要时让人工介入。很多系统为了保证这个可靠性,把补偿任务存在专门的持久化队列里,没执行成功就反复重试、直到人工干预。记住这三个提醒,补偿方案才不是"给自己挖坑"。

五、一句话算清什么时候用补偿/什么时候用 2PC

什么时候该走哪条路,用三问就能对号入座:一问你的业务允不允许短暂不一致——能,上补偿/Saga;不能,必须一步到位,老老实实回 2PC;二问你的事务是不是横跨多个长链路服务——是的话,2PC 把资源锁太久顶不住,上补偿;三问你能不能忍受"最后总能对",而不是"每个时刻都对"——用户下单扣个优惠,晚个一分钟最终一致完全没问题,Saga 轻装上阵;金融转账一分都错不得,老老实实上 2PC。用这三问比你读三页论文管用。

六、一个随手就能对号的补偿清单

把"补偿能不能接得住"落到一张审查清单上,新项目上 Saga 前过一遍,能少踩雷:

一问,每一步有没有配得上一个明确的"撤销动作"? 比如"扣库存"的撤销就是"回库存"、"扣款"的撤销就是"退款"。没有逆动作的步骤(像"发短信"补不了)就要么不放进 Saga,要么接受它不可逆。

二问,撤销动作是否写对了方向? 最容易写反的是"先扣库存再扣款"这件事——失败时必须按原序从后往前补偿,一步逆着补,就会把用户送到"钱付了库存却没了"或"免单当拣到"的坑里。

三问,补偿是不是幂等的? 网络会重试,同一个 Cancel 被触发两遍也必须安全,否则一次重试就多补偿一次、把账做糊。

四问,补偿本身失败了怎么办? 光有补偿动作不够,还要配持久化的补偿队列、超时告警与人工兜底入口,保证"补不动"也有人管。

把四问钉进方案评审,Saga 从"纸上的浪漫"落地成"能扛真实一路重试"的工程,就不远了。

把 Saga 压缩成一张 A4 的几行字:步骤带上可逆动作、失败按逆序回补、动作本身幂等、补偿要配可靠重试与人工兜底,这四行就是完整心法。把这四行贴到方案评审白板上,接得住长事务的补偿方案才不是纸上的浪漫,而是能扛住真实网络重试、超时与故障的工程底座——这也是"先放行、错再补"和"一步到位锁死"本质不同、却同样严肃的地方。

本节要点回顾

  • 补偿本质:每步配撤销动作,逆序回补,不锁死。
  • Saga 两式:编排式显眼好调试、协同式去中心能扛故障。
  • TCC 三段:Try 预占、Confirm 落定、Cancel 撤占,细粒度、侵入强。
  • 三条铁律:逆序、幂等、补偿本身要可靠。

Saga 用"先放行、错再补"给高并发长事务留了条活路。可这么多事务同时在一个"多版本"或"可重排"的世界里跑,怎么让读不挡写、写也不互相排队?下一节 5.5 我们用 MVCC 收官本章——多版本并发控制是怎么让并发更丝滑的。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U