Saga补偿风暴的触发链路与限流策略——分布式事务从2PC到Saga的选型实践

简介: TCC的Cancel执行了但Try根本没执行过——这是空回滚。Try在Cancel之后才到达——这是悬挂。Saga的补偿操作级联触发导致系统被补偿请求淹没——这是补偿风暴。三个问题有一个共同特征:代码在测试环境跑得好好的,一到生产就出问题,而且出问题时往往账已经对不上了。本文拆解TCC空回滚、悬挂、幂等三大坑的时序成因,给出事务记录表的解决方案,分析Saga补偿风暴的触发链路和限流策略。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

分布式事务有一个诡异的定律:测试环境跑一百遍都没事,上线第一分钟就翻车。

TCC的代码在测试环境跑了几百遍,Try、Confirm、Cancel三个接口都写得漂漂亮亮。上线之后,账对不上了。查了半天日志才发现——Cancel执行了,但Try根本没执行过。

这就是空回滚。今天把TCC和Saga的三个核心陷阱彻底拆开讲清楚。

先搞懂几个词:

TCC(Try-Confirm-Cancel) :分布式事务的一种实现模式。Try做资源预留,Confirm确认提交,Cancel取消回滚。相比2PC的同步阻塞,TCC不长时间持有锁,性能更好。

空回滚:Cancel执行了,但Try根本没执行过。

悬挂:Try在Cancel之后才到达,资源被冻结但没有对应的Confirm或Cancel来处理。

事务记录表:TCC解决空回滚和悬挂的核心工具。Try执行前先插入一条记录,Cancel执行前先查这条记录是否存在。

补偿风暴:Saga模式下,补偿操作级联触发,导致系统被补偿请求淹没。

一、TCC空回滚:Cancel执行了,但Try从没来过

时序成因

空回滚的触发链路是这样的:

  1. 调用方发起Try请求

  2. Try请求在网络中拥塞,超时了

  3. 调用方认为Try失败,触发Cancel

  4. Cancel执行,但Try根本没有执行过

  5. Try请求最终到达了——但Cancel已经执行完了

为什么会出问题?

Cancel的逻辑是“释放Try阶段预留的资源”。但如果Try根本没有执行,就没有资源被预留。Cancel执行时,会尝试“解冻”一笔不存在的冻结金额——如果代码写得不严谨,可能直接把账户余额给改了,造成数据不一致。

解决方案:事务记录表

核心思路是:Try执行前先记录,Cancel执行前先检查。

CREATE TABLE tcc_transaction (
    tx_id VARCHAR(64) PRIMARY KEY COMMENT '事务ID',
    status VARCHAR(16) NOT NULL COMMENT 'INIT/TRIED/CONFIRMED/CANCELLED',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
// Try:先标记,再冻结
public void try(String txId) {
   
    insert(txId, "INIT");
    freezeAmount(100);
    updateStatus(txId, "TRIED");
}

// Cancel:先检查Try是否执行过
public void cancel(String txId) {
   
    String status = queryStatus(txId);
    if (status == null) {
   
        // 空回滚:Try从未执行过,记录并返回成功
        insert(txId, "CANCELLED");
        return;
    }
    if ("CANCELLED".equals(status)) {
   
        // 幂等:已经回滚过了
        return;
    }
    unfreezeAmount(100);
    updateStatus(txId, "CANCELLED");
}

关键点:Cancel识别到空回滚后,不能直接返回成功——必须先记录一条CANCELLED记录。否则悬挂问题会紧随其后。

二、TCC悬挂:Cancel已经执行完了,Try才姗姗来迟

时序成因

悬挂的触发链路:

  1. Try请求在网络中拥塞,超时了

  2. 调用方触发Cancel,Cancel执行成功

  3. Try请求最终到达,开始执行业务操作

  4. 资源被冻结了,但事务管理器认为这个事务已经结束,不会再发Confirm或Cancel

  5. 冻结的资源永远无法释放

空回滚和悬挂是一对孪生问题。 空回滚是“Cancel先到了”,悬挂是“Try后到了”。两者都源于网络延迟导致的乱序。

解决方案

在Cancel空回滚返回成功之前,先记录该事务已回滚。Try接口在执行前检查这条记录:

// Try:检查是否已经被Cancel过
public void try(String txId) {
   
    String status = queryStatus(txId);
    if ("CANCELLED".equals(status)) {
   
        // 悬挂:Cancel已经执行过了,拒绝执行Try
        throw new RuntimeException("事务已被取消,拒绝执行Try");
    }
    insert(txId, "INIT");
    freezeAmount(100);
    updateStatus(txId, "TRIED");
}

空回滚和悬挂的完整防御需要三个检查:

场景 检查时机 检查内容 处理方式
空回滚 Cancel执行前 Try是否执行过 未执行过 → 记录CANCELLED,返回成功
悬挂 Try执行前 是否已被Cancel 已被Cancel → 拒绝执行Try
幂等 Confirm/Cancel执行前 是否已执行过 已执行过 → 直接返回成功

三、幂等:Confirm和Cancel可能被重复调用

TCC的三个接口(Try/Confirm/Cancel)都可能被重复调用。原因有两个:网络重试机制、事务管理器故障恢复后的重试。

解决方案:Confirm和Cancel执行前先查询事务状态。如果状态已经是CONFIRMED或CANCELLED,直接返回成功。

public void confirm(String txId) {
   
    String status = queryStatus(txId);
    if ("CONFIRMED".equals(status)) {
   
        return; // 幂等
    }
    commitAmount(100);
    updateStatus(txId, "CONFIRMED");
}

四、Saga补偿风暴:补偿链越长,风险越大

Saga模式和TCC不同。TCC是“预留+确认”,Saga是“直接执行+反向补偿”。每个本地事务执行后立即提交,失败时按反向顺序执行补偿操作。

补偿风暴的触发链路

Saga流程是:LT1 → LT2 → LT3 → ... → LTn。如果LTn失败,需要按反向顺序执行:CTn → CTn-1 → ... → CT2 → CT1。

补偿风暴发生在补偿操作本身也失败时。如果CTn-1补偿失败,事务管理器会重试。如果重试多次仍然失败,可能需要人工介入。更严重的是,如果补偿操作触发了新的Saga流程(比如补偿操作本身是一个跨服务调用),补偿链会级联扩散。

一个真实的场景:电商订单流程涉及下单、扣库存、扣积分、发优惠券、通知物流。如果第5步失败,需要依次补偿前4步。但如果“退优惠券”这个补偿操作本身也失败了(比如优惠券服务不可用),补偿链就卡住了。此时如果第5步的补偿触发了一个新的Saga流程,整个系统可能被补偿请求淹没。

解决方案

方案一:补偿操作加限流。 给补偿操作设置独立的线程池和限流阈值,避免补偿请求挤占正常业务资源。

方案二:补偿操作加退避。 补偿失败后不立即重试,而是等待一段时间后再试。重试间隔逐次递增。

方案三:补偿操作独立幂等。 补偿操作必须幂等——重复执行结果一致。这样即使重试多次,也不会造成数据错误。

方案四:设置补偿超时和人工介入阈值。 补偿操作超过一定次数后,转为人工处理,避免系统被无限重试拖垮。

五、小结

TCC的空回滚、悬挂、幂等三个问题有一个共同的根因:网络延迟导致的时序乱序。空回滚是Cancel先到了,悬挂是Try后到了,幂等是重复请求。解决方案的核心是事务记录表——Try执行前记录,Cancel执行前检查,Confirm/Cancel执行前查询状态。Saga的补偿风暴则源于补偿链的级联效应,需要通过限流、退避、独立幂等和人工介入阈值来控制。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
23天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9365 30
|
9天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
8天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
638 1
|
21天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2478 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
413 0
|
3天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
157 120
|
15天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
1874 0
|
10天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
1194 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面