1. 课程导论:为什么审批流需要事务补偿?
1.1 核心矛盾:长事务与短事务的本质差异
在企业级审批流中,一个典型的“采购订单审批”流程可能跨越多个微服务:订单服务、库存服务、预算服务、审批引擎、通知服务。传统数据库本地事务(ACID)只能保证单个服务内的强一致性,而审批流天然是跨服务、跨数据库、跨网络的长时间运行流程(通常持续数分钟到数天)。
关键认知: 审批流中的“事务”不是数据库事务,而是业务事务。业务事务允许中间状态,但必须保证最终一致性。一旦流程中途失败(如预算不足、审批人超时、服务宕机),必须通过事务补偿回滚已生效的副作用。
1.2 分布式事务 vs 本地事务:核心差异对比
| 维度 | 本地事务(ACID) | 分布式事务(BASE) |
|---|---|---|
| 范围 | 单个数据库 / 单个服务 | 跨服务、跨数据库、跨网络 |
| 一致性模型 | 强一致性(立即可见) | 最终一致性(允许短暂不一致) |
| 锁策略 | 行锁 / 表锁(短时间) | 无全局锁,通过补偿逻辑处理冲突 |
| 典型时长 | 毫秒 ~ 秒级 | 秒级 ~ 天级(含人工审批) |
| 失败处理 | 自动回滚(Rollback) | 手动或自动执行补偿事务(Compensation) |
| 适用场景 | 订单扣库存、转账 | 审批流、跨服务编排、Saga 流程 |
实践建议: 审批流中不要试图用“分布式强一致性”方案(如 2PC)锁住所有参与方,这会导致系统吞吐量急剧下降、死锁风险上升。正确的做法是采用补偿型事务,允许中间状态,但提供可靠的逆向操作。
1.3 为什么审批流必须引入事务补偿?
以一个典型的“费用报销审批流”为例,流程包含以下步骤:
- 提交申请 → 创建报销单(状态:待审批)
- 预算预占 → 从部门预算中冻结报销金额
- 审批节点 → 部门经理审批(可能耗时 2 小时)
- 审批节点 → 财务总监审批(可能耗时 1 天)
- 支付执行 → 调用支付服务打款
- 预算扣减 → 实际扣减预算,释放剩余额度
如果步骤 4(财务总监审批)驳回,则步骤 2 的“预算预占”必须被撤销(释放冻结金额)。如果没有补偿机制,预算将被永久占用,导致其他报销无法提交。这就是事务补偿的核心价值:撤销已生效的副作用,恢复系统到一致状态。
1.4 Saga 模式与 TCC 模式概览
业界两种最主流的分布式事务补偿模式:Saga 和 TCC。它们都基于补偿思想,但适用场景和实现复杂度不同。
1.4.1 Saga 模式:基于事件驱动的长流程补偿
Saga 将一个长事务拆分为多个本地事务,每个本地事务都有对应的补偿事务。当某个本地事务失败时,Saga 协调器按逆序调用之前所有成功事务的补偿操作。
- 适用场景: 审批流、订单流程、跨服务编排
- 优点: 无锁、高并发、适合长时间流程
- 缺点: 补偿逻辑需要手动实现,最终一致性窗口期存在
1.4.2 TCC 模式:预留-确认-取消的强约束补偿
TCC(Try-Confirm-Cancel)将每个服务操作分为三个阶段:
- Try: 预留资源(如冻结预算、锁定库存)
- Confirm: 确认执行(实际扣减预算、发货)
- Cancel: 取消预留(释放冻结资源)
TCC 要求每个参与方都实现 Try/Confirm/Cancel 三个接口,对业务侵入性较高,但能提供更强的资源管控。
选型提示: 审批流中,如果流程步骤少(2~3 步)且资源竞争激烈(如库存、预算),优先考虑 TCC;如果流程步骤多(5 步以上)且包含人工审批,Saga 更灵活。本课程后续章节将深入两种模式在审批流中的具体实现。
1.5 审批流事务补偿的核心设计原则
在后续章节深入实现之前,先建立三个关键设计原则:
| 原则 | 说明 | 反例(错误做法) |
|---|---|---|
| 幂等性 | 补偿操作可重复执行,多次执行结果与一次相同 | 补偿时直接 DELETE 记录,而非标记状态 |
| 可观测性 | 每个事务步骤的状态、补偿状态必须可追踪 | 没有事务日志,失败后无法定位 |
| 空补偿安全 | 补偿操作在 Try 未执行时也能安全执行 | 补偿时假设资源一定存在,导致空指针 |
课程预告: 本章建立了事务补偿的认知基础。第 2 章将深入 Saga 模式在审批流中的具体实现,包括状态机设计、补偿编排、超时处理等实战内容。