2. 回滚基础概念
在回滚保护机制设计中,所有复杂策略最终都建立在几个基础概念之上:事务、原子性、一致性、回滚点与补偿操作。它们不是抽象口号,而是决定“失败时系统能否回到可接受状态”的工程约束。理解这些术语之间的边界与配合方式,是后续设计回滚触发条件、保存点策略、补偿幂等性的前提。
一、核心术语的工程含义
事务是一组要么全部生效、要么全部不生效的操作集合。它强调边界:哪些操作属于同一个业务动作,哪些不属于。边界不清,回滚范围就会失控。例如“下单扣库存并生成支付单”应视为一个事务单元,而“发送营销短信”通常不应纳入同一事务,否则短信已发出却因后续失败被回滚,会造成不可逆的外部副作用。
原子性是事务最核心的性质:事务内的操作不可分割。工程上,原子性并不等于“数据库自动帮你搞定”,它需要日志、锁、版本号或补偿机制共同支撑。对于跨服务调用,原子性往往退化为“最终一致性 + 补偿”,而不是强原子。
一致性指事务执行前后,系统满足既定约束。注意,一致性是业务规则层面的,不是单纯的数据相等。例如账户余额不能为负、库存不能超卖、订单状态不能从“已取消”跳回“已支付”。回滚保护的目标,正是当异常发生时,把系统从“违反约束”拉回“满足约束”。
回滚点(Savepoint)是事务内部可回退的中间标记。它允许部分回滚:不必撤销整个事务,只撤销到某个保存点。典型场景是批量处理中,前 100 条已成功,第 101 条失败,可回滚到第 100 条后的保存点,保留已成功部分,避免全量重做。
补偿操作是面向已提交或不可回滚操作的“反向动作”。例如已扣款则退款、已扣库存则回补库存、已发消息则发更正通知。补偿不是回滚,它发生在原操作已经对外可见之后,因此必须考虑幂等、重试与顺序。
二、方法流程:从开启事务到补偿收尾
一个可落地的回滚保护流程通常包含以下步骤:
- 界定事务边界:明确哪些操作必须同生共死,哪些可以异步或补偿。
- 设置回滚点:在批量或长流程中,按批次或关键节点设置保存点,缩小回滚代价。
- 执行操作并记录日志:记录操作类型、参数、版本号、幂等键,为回滚或补偿提供依据。
- 异常判断:区分可重试异常、不可重试异常与业务约束冲突。
- 回滚或补偿:边界内回滚到保存点或事务起点;边界外触发补偿操作。
- 校验一致性:通过对账、状态机校验或约束检查确认系统已恢复一致。
关键原则:能回滚的不要补偿,能补偿的不要重做。回滚成本低于补偿,补偿成本低于人工介入。设计时应优先把操作纳入事务边界,其次才考虑补偿。
三、典型应用场景与参数对照
不同场景对回滚点与补偿的要求差异很大。下表给出常见场景的对照,便于在设计中快速定位策略。
| 场景 | 事务边界 | 回滚点策略 | 补偿操作 | 一致性要求 |
|---|---|---|---|---|
| 单库批量导入 | 单事务 | 每 500 条设保存点 | 无需补偿 | 强一致 |
| 订单 + 库存 + 支付 | 跨服务分布式事务 | 本地保存点 + 全局事务日志 | 退款、回补库存 | 最终一致 |
| 文件上传 + 元数据写入 | 对象存储 + 数据库 | 元数据写入前设保存点 | 删除已上传文件 | 最终一致 |
| 消息发送 + 状态更新 | 消息队列 + 数据库 | 状态更新后设保存点 | 发更正消息 | 最终一致 |
四、案例:批量任务中的保存点与补偿
假设一个批量结算任务需要处理 1000 笔订单,每笔订单包含“扣减余额”和“写入结算流水”两个操作。若不做保存点,第 999 笔失败会导致前 998 笔全部回滚,重做成本极高。合理做法是每 100 笔设置一个保存点:
BEGIN;
FOR i IN 1..1000 LOOP
IF i % 100 = 0 THEN
SAVEPOINT sp_batch_i;
END IF;
UPDATE account SET balance = balance - amt WHERE id = uid;
INSERT INTO settlement_log(order_id, amt) VALUES(oid, amt);
IF error THEN
ROLLBACK TO SAVEPOINT sp_batch_i;
-- 记录失败批次,继续下一批
END IF;
END LOOP;
COMMIT;
如果某些操作已经提交到外部系统(如已调用支付网关),则无法用数据库回滚撤销,必须走补偿:调用退款接口,并记录补偿日志。补偿接口必须支持幂等,通常以“订单号 + 补偿类型”作为幂等键。
常见误区:把“回滚”和“补偿”混为一谈。回滚发生在事务未提交或可撤销阶段,补偿发生在操作已对外可见之后。补偿失败不能简单重试,必须配合对账与人工兜底,否则会出现“退了两次款”或“库存回补但订单仍有效”等不一致。
五、复盘要点
- 边界优先:先画事务边界,再谈回滚点与补偿,避免把不可逆操作塞进事务。
- 保存点粒度:太粗则回滚代价大,太细则日志与锁开销高,通常按批次或业务阶段设置。
- 补偿幂等:补偿操作必须可重复执行且结果一致,否则重试会放大故障。
- 一致性校验:回滚或补偿后必须验证约束,不能默认“执行了就等于一致”。
- 可观测性:记录事务 ID、保存点名称、补偿状态,便于排障与对账。
掌握这些基础概念后,后续设计回滚保护机制时,才能准确判断“该回滚到哪、该补偿什么、如何验证一致性”,而不是把所有失败都交给一次全量重做。