分布式事务:从理论到实践的一致性解决方案
事务的 ACID 特性(原子性、一致性、隔离性、持久性)在单机数据库中通过锁机制和日志系统较易保证,但在分布式系统中,由于数据分散在多个节点,且节点间存在网络延迟、故障等不确定性,如何保证跨节点操作的原子性成为核心难题。本文将系统梳理分布式事务的理论模型、经典协议及工业界实践方案。
分布式事务的核心挑战
分布式事务的本质是跨节点操作的原子性:即多个节点的操作要么全部成功,要么全部失败,最终保证全局数据一致。其核心挑战源于:
- 网络不确定性:节点间通信可能超时、丢包,导致操作结果未知;
- 节点故障:部分节点宕机可能导致局部操作成功而其他节点失败;
- 性能与一致性权衡:强一致性方案往往伴随性能损耗,需根据业务场景取舍。
分布式事务规范与模型
1. X/Open DTP 模型:分布式事务的基础规范
The Open Group 提出的 X/Open DTP(Distributed Transaction Processing)模型定义了分布式事务的核心组件及交互方式,为实现分布式事务提供了标准框架。
核心组件:
- AP(Application Program):应用程序,定义事务边界(开始、提交、回滚),并发起对多个资源的操作(如跨库转账)。
- RM(Resource Manager):资源管理器,如数据库(MySQL、Oracle)、消息队列等,负责管理具体资源,并通过XA 接口向 TM 提供事务能力(提交、回滚)。
- TM(Transaction Manager):事务管理器,协调所有 RM,负责全局事务的决策(提交或回滚),并通过 XA 接口与 RM 通信,确保所有分支事务一致。
交互关系:
- AP 向 TM 发起全局事务,并调用 RM 的资源操作;
- TM 通过 XA 接口与 RM 通信,协调分支事务的准备、提交或回滚;
- RM 执行具体操作,并向 TM 反馈结果。
X/Open DTP 模型仅定义规范,未限定实现方式,TM 可通过 2PC、3PC 等协议实现协调。
2. XA 接口:RM 与 TM 的通信标准
XA 是 DTP 模型中定义的接口规范,用于 RM 向 TM 暴露事务控制能力,主要包含:
xa_start
:开始一个分支事务;xa_end
:结束分支事务;xa_prepare
:准备提交分支事务(2PC 的第一阶段);xa_commit
/xa_rollback
:提交或回滚分支事务(2PC 的第二阶段)。
支持 XA 的 RM(如 MySQL InnoDB、Oracle)可被 TM 统一协调,实现分布式事务。
经典协调协议:2PC 与 3PC
1. 两阶段提交(2PC:Two-Phase Commit)
2PC 是最经典的分布式事务协议,通过 “准备阶段” 和 “提交阶段” 确保所有 RM 的操作一致。
执行流程:
- 阶段一:准备(Prepare)
- TM 向所有 RM 发送 “准备请求”,询问是否可以提交分支事务;
- RM 执行事务操作(如 SQL 执行),但不提交,仅记录 Undo(回滚日志)和 Redo(提交日志);
- RM 若成功执行,反馈 “同意(Yes)”;若失败,反馈 “拒绝(No)”。
- 阶段二:提交(Commit)或回滚(Rollback)
- 若所有 RM 均反馈 “同意”,TM 向所有 RM 发送 “提交请求”,RM 执行提交并释放资源;
- 若任一 RM 反馈 “拒绝” 或超时,TM 向所有 RM 发送 “回滚请求”,RM 基于 Undo 日志回滚。
成功的情况
所有的资源全部成功才算是成功
阶段一
阶段二
失败的情况
准备阶段有一个资源失败就会失败
阶段一
阶段二
优点:原理简单,能保证强一致性。
缺点:
- 同步阻塞:准备阶段所有 RM 需锁定资源并等待 TM 决策,期间无法处理其他事务,性能低;
- 单点风险:TM 故障会导致 RM 长期阻塞(如等待提交 / 回滚指令);
- 数据不一致风险:若 TM 发送提交请求时部分 RM 未收到(如网络分区),会导致部分提交、部分未提交。
适用场景:对一致性要求极高、并发量低的场景(如金融核心交易)。
2. 三阶段提交(3PC:Three-Phase Commit)
3PC 是 2PC 的改进版,通过拆分准备阶段为 “CanCommit” 和 “PreCommit”,引入超时机制,减少阻塞风险。
执行流程:
- 阶段一:CanCommit
TM 向 RM 发送 “事务询问”,RM 仅判断是否可执行(不实际操作),反馈 Yes/No。 - 阶段二:PreCommit
- 若所有 RM 反馈 Yes,TM 发送 “预提交请求”,RM 执行事务操作(记录日志但不提交),并反馈 Ack;
- 若任一 RM 反馈 No 或超时,TM 发送 “中断请求”,RM 取消操作。
- 阶段三:DoCommit
- 若 TM 收到所有 Ack,发送 “提交请求”,RM 提交并反馈 Ack;
- 若 TM 未收到足够 Ack 或超时,发送 “回滚请求”,RM 回滚。
改进点:
- 引入超时机制:RM 在 PreCommit 阶段超时后会自动提交,减少因 TM 故障导致的阻塞;
- 拆分准备阶段:降低资源锁定时间,提升性能。
缺点:
- 仍可能因网络分区导致不一致(如部分 RM 在 PreCommit 后未收到 DoCommit,超时自动提交,而其他 RM 收到回滚);
- 协议更复杂,实际应用较少。
工业界实践:柔性事务方案
2PC/3PC 属于 “刚性事务”,强一致性但性能差,难以满足高并发场景。工业界更多采用 “柔性事务”,通过最终一致性保证业务可用,典型方案如下:
1. 本地消息表(可靠消息 + 最终一致性)
核心思想:将分布式事务拆分为 “本地事务” 和 “消息通知”,通过消息队列确保跨节点操作最终一致。
流程:
- 本地事务 + 消息存储:
- 应用在本地数据库中创建 “消息表”,记录需要发送的跨节点操作;
- 执行本地业务逻辑(如扣减库存),并在同一事务中向消息表插入 “待发送” 消息(本地事务保证两者原子性)。
- 消息发送与确认:
- 定时任务扫描消息表,将 “待发送” 消息推送至消息队列(如 RabbitMQ、Kafka);
- 接收方消费消息,执行远程操作(如创建订单),完成后向消息队列发送 “确认”;
- 发送方收到确认后,将消息表中对应消息标记为 “已完成”。
- 失败重试:
- 若接收方操作失败,消息队列会重新投递(需保证幂等性);
- 若发送方未收到确认,定时任务会重新推送消息。
优点:实现简单,无锁阻塞,性能高。
缺点:需侵入业务代码(消息表操作),消息表需与业务表在同一数据库。
适用场景:非核心业务(如订单通知、日志同步),允许短暂不一致。
2. TCC(Try-Confirm-Cancel)
核心思想:通过业务层逻辑将分布式事务拆分为 “尝试、确认、取消” 三个阶段,实现最终一致性。
流程:
- Try:资源检查与预留(如冻结库存、锁定资金),确保后续操作可行;
- Confirm:确认执行(如扣减冻结的库存、转账),仅在所有 Try 成功后执行;
- Cancel:取消操作(如解冻库存、释放资金),若任一 Try 失败则执行。
示例:跨银行转账(A 向 B 转账 100 元)
- Try:检查 A 余额≥100,冻结 A 的 100 元;检查 B 账户状态正常。
- Confirm:扣减 A 的 100 元,增加 B 的 100 元,解冻资金。
- Cancel:解冻 A 的 100 元。
优点:无锁阻塞,性能优异,适合高并发场景。
缺点:需业务层实现 Try/Confirm/Cancel 接口,开发成本高;需保证 Confirm 和 Cancel 的幂等性与可补偿性。
适用场景:核心业务(如电商下单、支付),对性能要求高。
3. SAGA 模式
核心思想:将长事务拆分为多个短事务(本地事务),通过 “正向流程” 和 “反向补偿” 保证最终一致。
流程:
- 定义事务步骤:T1→T2→…→Tn(每个 Ti 为本地事务);
- 若所有 Ti 执行成功,事务完成;
- 若某 Ti 失败,执行反向补偿:Cn→…→C2→C1(Ci 为 Ti 的逆操作,如 Ti 是 “创建订单”,Ci 是 “取消订单”)。
示例:订单履约流程(创建订单→扣减库存→支付→发货)
- 若 “支付” 失败,执行补偿:取消发货→恢复库存→取消订单。
优点:支持长事务,适合流程化业务。
缺点:补偿逻辑复杂(需处理部分成功场景);可能产生中间状态(如订单显示 “已创建” 但未支付)。
适用场景:分布式流程(如订单履约、供应链管理)。
方案对比与选型
方案 | 一致性 | 性能 | 开发成本 | 适用场景 |
---|---|---|---|---|
2PC | 强一致 | 低 | 低 | 金融核心交易(如转账) |
本地消息表 | 最终一致 | 高 | 中 | 非核心业务(如通知、日志) |
TCC | 最终一致 | 高 | 高 | 高并发核心业务(如电商下单) |
SAGA | 最终一致 | 中 | 中 | 长事务流程(如订单履约) |
v1.3.10