分布式事务与一致性模型深度实战:从 2PC 到 Saga 的工程决策

1. 引言:为什么分布式事务如此之难?

在单体数据库时代,事务的 ACID 特性由数据库引擎天然保障。一旦系统演进到微服务架构,一次业务操作可能横跨多个数据库、消息队列、第三方服务,传统的事务机制便力不从心。分布式事务的核心挑战在于:网络不可靠、节点可能故障、时钟不同步。

本文将从理论出发,深入剖析 2PC、3PC、TCC、Saga、事件溯源、最终一致性等主流方案,并给出工程实践中的选型决策和避坑指南。

2. CAP 定理与一致性光谱

Brewer 的 CAP 定理指出,在一个分布式系统中,一致性 (Consistency)、可用性 (Availability)、分区容忍性 (Partition tolerance) 三者不可兼得,最多只能同时满足其中两项。

  • CP 系统:保证一致性和分区容忍性,但在网络分区时可能拒绝服务(如 ZooKeeper、etcd)
  • AP 系统:保证可用性和分区容忍性,但可能返回过时数据(如 Cassandra、DynamoDB)
  • CA 系统:传统关系型数据库,无法真正容忍网络分区

实际工程中,一致性是一个光谱而非二元选择:

一致性级别描述典型用例
强一致性写入后立即可读库存扣减、转账
线性一致性所有操作按全局实时顺序分布式锁、Leader 选举
顺序一致性所有节点看到相同操作顺序Replication 协议
因果一致性因果相关操作有序评论回复、消息
最终一致性若无新写入,最终一致DNS、推荐系统

3. 两阶段提交 (2PC)

3.1 协议流程

2PC 是最经典的分布式事务协议,将事务分为两个阶段:

阶段一:投票阶段 (Voting Phase)

  1. 协调者向所有参与者发送 Prepare 请求
  2. 参与者执行事务操作(写入 undo/redo log),但不提交
  3. 参与者返回 Yes(成功)或 No(失败)

阶段二:提交阶段 (Commit Phase)

  1. 若所有参与者返回 Yes,协调者发送 Commit 指令
  2. 若有任何参与者返回 No,协调者发送 Rollback 指令
  3. 参与者执行提交或回滚,返回 ACK

3.2 故障模式分析

  • 参与者故障:协调者超时未收到回复,回滚事务
  • 协调者故障(阶段一):新协调者回滚事务(保守策略)
  • 协调者故障(阶段二):参与者处于 Prepared 状态,阻塞等待——这是 2PC 最大的问题
  • 网络分区:可能导致部分提交部分回滚的不一致

3.3 工程缺陷

2PC 的主要问题在于同步阻塞和单点故障:

  • 参与者在 Prepare 阶段后持有锁,若协调者故障则长时间阻塞
  • 协调者是单点,故障后整个系统停滞
  • 两阶段之间的延迟导致事务吞吐量受限

4. 三阶段提交 (3PC)

3PC 试图解决 2PC 的阻塞问题,将流程拆分为三个阶段:

  1. CanCommit:协调者询问参与者是否可以执行(不持锁)
  2. PreCommit:参与者执行操作并写日志
  3. DoCommit:参与者提交事务

3PC 引入了超时机制:参与者在 PreCommit 后若未收到 DoCommit,会默认提交而非阻塞。但这带来了网络分区下的不一致风险:当协调者与参与者网络断开时,参与者自行提交,若协调者回滚,则产生脑裂。

实际上 3PC 并未被广泛采用,其复杂度增加而收益有限,多数工程实践选择其他方案。

5. TCC (Try-Confirm-Cancel)

5.1 核心思想

TCC 是一种补偿型分布式事务方案,将每个参与者的业务操作拆分为三个方法:

  • Try:预留资源(如冻结库存、预扣余额)
  • Confirm:确认执行(如实际扣减库存、扣款)
  • Cancel:取消执行(如释放冻结库存、退还预扣余额)

5.2 执行流程

  1. 主业务方调用所有参与者的 Try 方法
  2. 若所有 Try 成功,依次调用 Confirm
  3. 若任一 Try 失败,对已成功的参与者调用 Cancel

5.3 工程陷阱

空回滚问题:Try 阶段超时,协调者发起 Cancel,但参与者尚未执行 Try。Cancel 方法需要能正确处理"还没有 Try 就收到 Cancel"的情况,正确做法是识别并跳过空回滚。

悬挂问题:Try 阶段因网络延迟晚于 Cancel 到达。参与者 Cancel 后收到 Try,导致资源被不当占用。解决方案:记录 Cancel 已执行,迟到 Try 直接拒绝。

幂等性:Confirm 和 Cancel 可能被重复调用(网络重试、幂等重试),必须保证多次调用结果一致。通常使用事务状态表实现幂等控制。

5.4 适用场景

TCC 适合业务可补偿、资源预留明确的场景,如电商下单(库存冻结→扣减→释放)、金融转账(预扣→划拨→退还)。

6. Saga 模式

6.1 核心思想

Saga 将一个长事务拆分为多个本地短事务,每个短事务有对应的补偿操作。若某步骤失败,按逆序执行补偿操作回滚。

编排式 Saga (Choreography):无中心协调者,每个服务完成后发布事件,下游服务监听事件并执行操作。

命令式 Saga (Orchestration):由 Saga Orchestrator 集中管理事务流程,依次调用各服务的操作,失败时调用补偿。

6.2 编排式 vs 命令式

维度编排式 Choreography命令式 Orchestration
复杂度去中心化,逻辑分散集中管理,职责清晰
耦合度服务间通过事件耦合依赖 Orchestrator
可观测性难以追踪全程状态Orchestrator 掌握完整状态
适用场景简单工作流、步骤少复杂业务流程、需状态管理

6.3 Saga 的隔离性缺陷

Saga 缺乏隔离性——每个步骤提交后,其他事务就能看到中间状态。解决方案包括:

  • 语义锁 (Semantic Lock):在业务层面标记数据状态(如 "pending"),其他事务需检查状态
  • 交换式更新 (Commutative Updates):设计可交换的操作顺序
  • 乐观锁:通过版本号检测并发冲突

7. 事件溯源 (Event Sourcing)

事件溯源是一种架构模式:不存储实体的当前状态,而是存储导致状态变化的所有事件序列。当前状态通过重放事件重建。

事件溯源与分布式事务

在分布式事务场景中,事件溯源常与 Saga 结合:

  1. 服务执行本地事务并发布领域事件
  2. 下游服务订阅事件并执行操作
  3. 失败时发布补偿事件触发上游补偿

Event Sourcing CQRS 的组合在现代分布式系统中越来越流行,如 Axon Framework、EventStoreDB。

8. 最终一致性模式

8.1 可靠事件队列

核心思路:将分布式事务拆解为本地事务 消息发送,通过消息队列实现跨服务通信。

实现方案:本地消息表

  1. 业务执行和消息插入在同一本地事务中
  2. 独立的消息投递服务轮询消息表并投递到 MQ
  3. 下游消费消息并执行业务逻辑
  4. 通过重试保证消息最终被消费

8.2 事务消息 (RocketMQ 模式)

RocketMQ 的事务消息将消息发送分为两个阶段:

  1. 发送 Half Message(对消费者不可见)
  2. 执行本地事务,提交或回滚 Half Message
  3. MQ 回查本地事务状态(防止执行过程中崩溃)

8.3 最大努力通知

适用于对一致性要求不严格的场景:

  • 执行方尽力向通知方发送结果通知
  • 通知方可主动查询执行方的状态
  • 允许失败,但提供查询和补偿机制

9. 选型决策矩阵

方案一致性性能复杂度适用场景
2PC强低低跨库事务、强一致、容忍阻塞
TCC强(业务层)中高电商、金融、可补偿业务
Saga最终高中长流程、微服务编排
事件溯源 事件驱动最终高高复杂业务、审计需求
可靠事件队列最终高中异步通知、弱一致容忍
本地消息表最终高低简单跨服务调用

10. 工程实战:避坑指南

10.1 幂等性设计

任何分布式事务方案都必须处理重复请求。核心手段:

  • 全局唯一 ID(雪花 UUID)去重
  • 数据库唯一约束(业务键 事务 ID)
  • Redis SetNX 分布式防重
  • 状态机 乐观锁(CAS 更新)

10.2 超时与重试策略

  • 设置合理的超时时间(参考 P99 延迟)
  • 指数退避重试 抖动(避免 thundering herd)
  • 重试上限与熔断机制
  • 区分可重试错误和不可重试错误

10.3 监控与告警

  • 事务成功率、超时率、回滚率
  • 各阶段耗时分布(P50/P95/P99)
  • 悬挂事务检测与自动恢复
  • 定时对账(检查数据一致性)

10.4 数据对账

无论采用何种方案,数据对账都是最后一道防线:

  • 定时扫描事务状态表,找出未完成的事务
  • 与上下游系统比对数据快照
  • 提供自助修复工具和手动补偿流程

11. 实战案例:电商下单的分布式事务实现

考虑一个电商下单场景:用户下单涉及库存扣减、积分增加、优惠券核销、订单创建四个服务。

Saga 实现方案:

  1. 订单服务:创建订单(状态=PENDING),发布 OrderCreated 事件
  2. 库存服务:扣减库存,发布 StockReserved 事件;释放库存补偿
  3. 优惠券服务:核销优惠券,发布 CouponUsed 事件;恢复优惠券补偿
  4. 积分服务:增加积分,发布 PointsAdded 事件;扣减积分补偿

若积分服务增加失败,Saga Orchestrator 发起补偿:

  1. 恢复优惠券(Cancel CouponUsed)
  2. 释放库存(Cancel StockReserved)
  3. 更新订单状态为 FAILED

12. 前沿趋势

  • Percolator 模型 (TiDB):基于 MVCC 分布式锁的乐观事务实现
  • Calvin 模型 (FaunaDB):确定性事务调度,所有副本按相同顺序执行
  • 混合逻辑时钟 (HLC):无需全局时钟的因果一致性方案
  • Serverless 事务:云原生架构下的事务编排(AWS Step Functions、Azure Durable Functions)

13. 总结

分布式事务没有银弹,选型的核心原则是:

  1. 能不用就不用:通过业务设计避免分布式事务(如合并服务、数据冗余)
  2. 能最终一致就最终一致:大多数业务场景容忍短暂不一致
  3. 能补偿就不锁定:Saga/TCC 比 2PC 更适合高并发场景
  4. 必须有兜底:数据对账是分布式事务的最后防线

理解每种方案的权衡(trade-off),根据业务特征做出合理的工程取舍,才是分布式事务实战的精髓。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部