连载中 5/18

TCC 三大坑:空回滚、幂等、悬挂

2026-06-16 · 4851 阅读 · 0 评论 · 0 赞

上线一周的三个工单

TCC 框架搭好、三接口写完,老王上线一周收到三个灵异工单:一单报「冻结记录不存在」;一单被扣了两次钱;最邪的一单,Cancel 已经把钱退了,几秒后 Try 又把 100 元冻上了,这笔钱既不能确认也没人再取消,永久悬着。三个现象对应 TCC 的三大坑:空回滚、幂等、悬挂。根源是同一个——第 1 篇讲的物理现实:远程调用的时序不可控。

坑一:空回滚——Cancel 比 Try 先到

时序还原:发起方调账户服务 Try,请求在网络里丢了;框架超时判定整个事务失败,触发回滚,Cancel 指令发出——Cancel 比 Try 先抵达。账户服务执行 Cancel:查冻结记录,没有。第一版代码直接抛异常,结果框架一看 Cancel 失败,按协议持续重试——永远失败,事务卡死。

解法:Cancel 执行前先查「Try 是否执行过」;没执行过,说明是空回滚,直接返回成功并落一条空回滚标记。为什么必须落标记而不只是返回成功?因为 Try 可能还在路上——这就引出坑三。

坑二:幂等——重试是常态不是异常

Confirm 调账户服务扣款成功,但响应包在网络里丢了;框架超时,按协议重试 Confirm——第二遍又扣了 100。Cancel 同理可能重复解冻。TCC 框架对 Confirm/Cancel 的重试没有次数上限、没有「只调一次」的承诺,重试是协议的一部分。

解法:每个分支接口执行前查「这个分支是否已执行过」,执行过直接返回上一次的结果。判断依据可以是数据库唯一键(同一全局事务 ID + 分支 ID 的执行记录只允许插一条),也可以是状态机的状态判断(已在 CONFIRMED 状态的 Confirm 请求直接幂等返回)。重复调用千千万,业务动作只做一次。

坑三:悬挂——迟到的 Try

把坑一的时序接着推演:Cancel 已经处理完(事务已回滚),那个迷路的 Try 包此刻才姗姗抵达账户服务。服务不设防的话,Try 忠实地执行了冻结——可全局事务已经结束,不会有任何人来 Confirm 它,也不会有第二次 Cancel 来解冻它。100 元冻结悬在账户里,用户余额少了一块,投诉直接打到客服。这就是悬挂:预留了资源,事务已死,无人认领。

解法:Try 执行前先查「该分支是否已回滚(存在空回滚或 Cancel 记录)」;是,说明事务已死,拒绝 Try。这就是坑一里「必须落空回滚标记」的原因——标记就是给迟到的 Try 看的门禁。

统一解法:一张事务控制表

三个坑的防御信息,归拢到一张表就够了:

tcc_control(全局事务ID, 分支ID, 状态, 时间)
状态取值:TRIED / CONFIRMED / CANCELLED
Try     执行前:查到 CANCELLED?拒绝(防悬挂)
Cancel  执行前:无 TRIED 记录?落空回滚标记并返回成功(防空回滚)
        已有 CANCELLED 记录?幂等返回(防重试)
Confirm 执行前:已有 CONFIRMED 记录?幂等返回(防重试)

三个接口的入口都变成同一段套路:查状态、判断、再干活。主流框架(Seata TCC 模式、ByteTCC)都内置了这层防护,但前提是你理解它防什么——面试和故障排查时,「为什么我框架都接了还撞号」的问题,答案全在这张表里。

防御清单

老王把三坑整理成接口上线前的四问:一问 Cancel 遇到无 Try 记录是否幂等返回并落标记;二问 Confirm/Cancel 重复调用是否只生效一次;三问 Try 是否校验事务已回滚状态;四问 状态记录与业务操作是否同库同事务(防「状态落了、钱没动」的半提交)。四问全绿,TCC 才算能出门。

小结

三大坑一句话:空回滚靠标记防,幂等靠状态防,悬挂靠门禁防;一张事务控制表 + 三个接口统一的查状态套路,就是 TCC 的全部防御工事。TCC 适合短事务的资源预留,可一旦流程变长、参与方变多——退货要过五个环节、审批要跨三天——每个环节都写 Try/Confirm/Cancel 会把团队逼疯。长流程需要另一种组织方式:Saga 状态机与补偿,下一篇见。

503

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

#分布式事务#TCC#空回滚#幂等#悬挂

评论 (0)

相关推荐

连载中 12/20

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

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

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 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 赞