| 方案 | 一致性 | 性能 | 侵入性 | 适用场景 | 代表框架 |
|---|---|---|---|---|---|
| 2PC/3PC | 强一致 | 低 | 低 | 金融转账、跨库强一致 | Atomikos, MySQL XA |
| TCC | 最终一致 | 高 | 高 | 高并发短事务(支付/库存) | Seata, ByteTCC |
| Saga | 最终一致 | 中 | 中 | 长流程业务(订单履约/旅行) | Netflix Conductor, Seata |
| 本地消息表 | 最终一致 | 中 | 中 | 异步数据同步、解耦场景 | 自研+数据库 |
| 事务消息 | 最终一致 | 高 | 低 | MQ支持的异步解耦(支付通知) | RocketMQ, Kafka |
⚙️ 一、强一致性方案(牺牲可用性,保证数据强一致)
2PC(两阶段提交)
原理:协调者分两阶段管理事务:
两阶段提交协议(Two-Phase Commit, 2PC)是分布式事务中保证原子性与一致性的核心协议,通过协调者与参与者的交互实现跨节点事务的“全成功”或“全失败”。以下是其原理的详细解析:
⚙️ 一、核心角色与设计目标
- 协调者(Coordinator)
-
- 事务的全局管理者,负责发起事务、收集参与者响应并决策提交或回滚
- 参与者(Participant)
- 实际执行本地事务的资源管理器(如数据库、消息队列),向协调者反馈本地事务状态
- 设计目标 :
解决分布式环境下多个独立资源的事务原子性问题(例如:跨行转账需同时扣款与到账)
🔄 二、两阶段执行流程
阶段1:准备阶段(Prepare Phase)
- 协调者操作 :
-
- 向所有参与者发送 Prepare请求,携带全局事务ID(XID)
- 参与者操作 :
-
- 执行本地事务 (如执行SQL更新),但 不提交 。
- 记录Undo/Redo日志 :用于回滚或恢复(如MySQL的redo log标记为prepare状态)。
- 锁定资源 :防止其他事务修改(如锁定账户余额)
- 反馈结果:成功则返回 Yes,失败或超时则返回 No
阶段2:提交阶段(Commit Phase)
- 协调者决策 ****:
-
- 全部参与者返回 Yes :发送 Commit指令。
- 任一参与者返回 No****或超时 ****:发送 Rollback指令
- 参与者执行 ****:
-
- 收到 Commit:提交本地事务,释放锁,返回确认(ACK)。
- 收到 Rollback:利用Undo日志回滚,释放锁
优点:强一致性,实现简单(如数据库支持XA协议)。
缺点:
1、同步阻塞(性能问题)
资源长期锁定:在准备阶段(Prepare Phase),所有参与者需锁定本地资源并等待协调者指令。在此期间,其他事务无法访问被锁资源,导致系统吞吐量急剧下降
全局等待:协调者需收集所有参与者的响应后才能进入下一阶段。任一参与者的响应延迟或阻塞,会导致整个事务链停滞,类似“全队等一人”的阻塞场景 并发性能差:高并发场景下,大量事务因等待资源锁而排队,系统响应时间显著增加,难以支撑高吞吐需求
2、单点故障(协调者宕机导致事务挂起)
协调者宕机导致事务悬挂:若协调者在第二阶段(Commit Phase)崩溃,参与者将永久阻塞于“已准备但未决”状态,资源锁无法释放,系统可能陷入死锁。 需要重新选去事务协调者。
3、数据不一致
网络分区引发分裂:协调者发出提交指令后,若网络故障导致部分参与者未收到COMMIT,已提交的参与者与未提交者之间会产生数据分歧。
场景:
数据库集群分库,多个数据库数据一致性,对一致性要求极高的金融核心业务。
3PC(三阶段提交)
- CanCommit 阶段(询问阶段)
协调者动作:向所有参与者发送事务询问请求(CanCommit),询问是否具备执行条件(如资源可用性、锁能否获取)。
参与者动作:
检查自身状态(如资源、锁、业务规则),不执行事务操作,仅做可行性预判。
若通过检查,返回 Yes并进入预备状态;否则返回 No。
特点:此阶段不锁定资源,轻量级操作,避免无效资源占用。
- PreCommit 阶段(预提交阶段)
协调者决策:
全部返回 Yes:发送 PreCommit指令,进入预提交状态。
任一返回 No或超时:发送 Abort指令中止事务。
参与者动作:
收到 PreCommit后:
执行事务操作(如更新数据),但不提交。
记录 undo/redo 日志,用于故障恢复。
锁定资源,返回 ACK响应。
收到 Abort或超时未响应:立即中止事务并释放资源。
- DoCommit 阶段(提交阶段)
协调者决策:
收到所有 ACK:发送 DoCommit指令。
未收到全部 ACK或超时:发送 Abort指令。
参与者动作:
收到 DoCommit:提交事务,释放资源锁,返回 ACK。
收到 Abort或超时未收到指令:
利用 undo 日志回滚事务。
超时自动提交:因 PreCommit 已达成共识,参与者默认提交(关键改进点)
- 超时自动决策机制
参与者超时行为:
CanCommit 超时 → 自动中止。
PreCommit 超时 → 自动中止。
DoCommit 超时 → 自动提交(因 PreCommit 已确认全局可提交)。
作用:避免因协调者故障导致参与者无限阻塞,显著减少死锁风险。
- 预提交状态(PreCommit)的引入
意义:
在 DoCommit 前明确所有参与者已就绪(通过 PreCommit 确认),降低“不确定状态”持续时间。
参与者记录 undo/redo 日志,确保故障后可恢复至一致状态。
- 非阻塞设计
协调者故障时,参与者可通过超时机制自主决策(提交或回滚),而无需等待协调者恢复,提升系统可用性
优点:
- 降低阻塞风险 ****:超时机制和自主决策避免资源长期锁定。
- 增强容错性 ****:协调者故障时参与者可继续推进事务。
缺点: 额外网络开销:三阶段通信比 2PC 多一轮交互,延迟更高 若 DoCommit 阶段网络分区,部分参与者超时提交,另一部分收到 Abort 回滚,导致数据分裂
使用场景: 弱强一致性,网络不稳定,超过1秒的事务。
分阶段事务的缺点
致命缺点
也是比较致命的缺点,强依赖数据库事务,XA协议。
二、最终一致性方案(保证可用性,容忍短暂不一致)
TCC(Try-Confirm-Cancel)是一种基于业务补偿的分布式事务解决方案,通过将事务拆分为 Try(尝试)、Confirm(确认)、Cancel(取消) 三个阶段实现最终一致性。以下是其核心原理及关键设计要点:
⚙️ **一、TCC 的核心三阶段
- Try (尝试阶段)
-
- 目标 ****:预留资源并完成业务检查,不执行实际数据修改。
- 操作 ****:
-
- 冻结资源(如库存冻结、账户资金预扣)
- 生成预记录(如创建状态为"待确认"的订单)
- 特性 ****:
-
- 幂等性 ****:支持重复调用(如网络重试)
- 隔离性 ****:通过资源预留防止脏读(如冻结库存后,其他事务无法占用)
- Confirm (确认阶段)
-
- 触发条件 ****:Try阶段所有参与者均成功。
- 操作 ****:
-
- 使用Try阶段预留的资源执行业务(如扣减冻结库存、确认订单生效)
- 特性 ****:
-
- 幂等性 ****:避免重复提交(如通过事务ID标记状态)。
- 无业务检查 ****:Try阶段已确保资源可用性,Confirm只需提交。
- Cancel (取消阶段)
-
- 触发条件 ****:Try阶段任一参与者失败或超时。
- 操作 ****:
-
- 释放预留资源(如解冻库存、删除预订单)
- 特性 ****:
-
- 幂等性 **:支持重复回滚
- 空回滚处理 ****:Try未执行时,Cancel需跳过操作(通过日志检查)
问题
- 空回滚( Empty Rollback )
-
- 场景 ****:Try未执行(如网络超时),但协调者触发Cancel
- 解决 ****:记录Try阶段日志,Cancel时检查无日志则跳过操作
- 防悬挂( Anti-Suspension )
-
- 场景 ****:Cancel先执行成功,Try后到达导致资源悬挂
- 解决 ****:Cancel后记录状态,Try执行前检查是否已被取消
- 幂等控制
-
- 方法 ****:为每个事务分配唯一ID,记录操作状态,重复请求直接返回历史结果
✅ TCC 的核心优点
⏱️ *高性能与低资源阻塞
短时资源锁定
Try 阶段仅 预留资源(如冻结库存、预扣资金),不直接修改业务数据,资源锁定时间极短,显著减少并发冲突
无全局锁机制
TCC 通过业务层逻辑实现资源隔离(如预扣表、版本号控制),而非依赖数据库锁。这种设计允许不同事务在不同资源节点上并发执行,显著提升吞吐量
异步提交能力
Confirm/Cancel 阶段可异步执行,避免同步阻塞,支持高吞吐场景(如电商秒杀)。不像2pc和3pc那样强依赖数据库。做数据库资源占用,所以异步问题不大
🛡️ 高容错性与最终一致性*
- 幂等性保障 ****:每个阶段支持重试,通过事务 ID 和状态日志确保重复操作不产生副作用
- 故障恢复机制 ****:即使 Confirm/Cancel 阶段失败,可通过日志重试或人工干预恢复数据一致性
✅ TCC 的核心缺点
- 💻 开发成本高 & 业务侵入性强
-
- 三阶段逻辑需手动实现 ****:每个业务需独立编写 Try、Confirm、Cancel 接口,代码量增加 2–3 倍(如订单创建需分别实现库存预留、确认扣减、释放预留)
- 与业务逻辑耦合 ****:事务代码嵌入业务层,重构或扩展时代价较高
- 🔄 ****补偿机制复杂度高
-
- 回滚逻辑复杂 ****:Cancel 阶段需精准释放多类型资源(如同时回滚数据库更新、撤回 MQ 消息、恢复缓存),设计不当易导致数据不一致
- 异常场景处理繁琐 ****:需额外处理以下问题:
-
- 空回滚 ****:未执行 Try 却触发 Cancel(需通过事务 ID 校验跳过操作)
- 防悬挂 ****:Cancel 先于 Try 执行时,需阻止后续 Try 操作
- 📉 ****运维与监控挑战
-
- 事务状态跟踪难 **:需维护全局事务日志(如 Redis 或 DB 记录 XID 状态),增加系统复杂度
- 长事务风险 ****:若 Confirm/Cancel 阶段延迟,预留资源可能长期占用(如冻结资金未及时释放)
✅ TCC 使用场景 高并发短事务,跨异构资源事务,和2cp,3cp一直都是并行调用参与者
电商下单、秒杀库存(Try 阶段快速预留,Confirm 异步提交,组合支付(银行卡+积分)、多存储系统(DB + Redis
**一、Saga
1. 事务拆分与补偿链
- 正向事务链 :将全局事务拆分为多个连续的本地子事务(如电商下单流程:创建订单 → 扣减库存 → 支付扣款)
- 补偿事务链 :为每个正向操作定义逆向补偿操作(如支付失败 → 退款 → 恢复库存 → 取消订单),形成反向补偿链。
2. 执行规则
- 成功场景 :顺序执行所有子事务(T₁ → T₂ → T₃)。
- 失败场景 :若子事务 Tᵢ 失败,则按 逆序执行补偿操作 (Cᵢ → Cᵢ₋₁ → … → C₁)
3. 最终一致性保证
- 通过重试机制和补偿操作,确保系统最终达到一致状态,但允许中间状态短暂不一致(如订单显示“处理中”但库存未扣减)
一、Saga 事务的核心优点
将长事务拆分为多个独立子事务,各服务仅需关注本地逻辑,降低耦合度,
在 Saga 模式中, 并行子事务的回滚触发机制 需要结合事务依赖关系、补偿策略和状态管理来实现。以下是其核心流程与关键设计:
一、 并行回滚的触发条件
- 任一子事务失败
-
- 当某个并行执行的子事务 执行失败 (如超时、网络异常、业务校验不通过),Saga 会立即触发全局回滚流程。
- 补偿失败超时
-
-
若补偿操作(Compensating Action)执行失败且超过重试阈值,部分 Saga 实现(如 Seata)会强制标记事务为失败,需人工介入处理。
-
二、 回滚触发流程
1. 基于事件驱动的 Saga(无协调器)
- 步骤 :
-
- 子事务 A 和 B 并行执行,均成功后发布 TransactionSucceeded事件。
- 若子事务 A 失败,发布 TransactionFailed事件。
- 其他参与者(如 B、C)监听失败事件,触发对应的补偿操作(如 CancelB、CancelC)。
- 特点 :
-
- 无全局状态管理 ,依赖事件广播实现补偿链式触发。
- 补偿顺序不确定 ,需业务自行保证逆向依赖关系(如 B 依赖 A,则 A 失败需先补偿 B)。
2. 基于协调器的 Saga(如 Seata、dtm)
- 步骤 :
-
- 协调器记录并行子事务的执行状态(如成功/失败)。
- 检测到任一子事务失败,协调器标记全局事务为 Abort。
- 按 反向拓扑顺序 触发补偿(如先补偿 C,再补偿 B,最后补偿 A)。
- 特点 :
-
- 状态集中管理 ,确保补偿顺序正确。
- 支持保存点(Savepoint),允许从中间状态恢复(如重试部分操作)。
- ⏱️ 高性能与低资源阻塞
-
- 无全局锁 :每个本地事务提交后立即释放资源(如数据库连接、行锁),避免长时间阻塞,显著提升吞吐量(如电商系统可支持万级 TPS)
- 异步执行 :支持编排式(Orchestration)或协同式(Choreography)实现异步流程,减少同步等待时间
2 、 🌐 灵活适配复杂业务
-
- 跨异构系统 :可整合数据库、消息队列、第三方 API 等异构资源(如同时操作 MySQL 和 Redis)37。
- 长流程支持 :天然适合多步骤业务流程(如电商订单:创建→扣库存→支付→发货),允许动态调整执行路径110。
- 异步解耦 将长事务拆分为多个独立子事务,各服务仅需关注本地逻辑,降低耦合度。单个子事务失败不会导致全局回滚,仅触发局部补偿
3、 ⚙️ 高容错性
-
- 部分服务宕机时,可通过重试机制或人工介入恢复,避免全局阻塞
二、Saga 事务的核心缺点
- 🧩 补偿逻辑复杂
-
- 开发成本高 :需为每个正向操作编写逆向补偿逻辑(如“支付成功但物流发货失败”需触发“退货退款”),代码量增加 30% 以上
- 不可逆操作风险 :若补偿无法执行(如已发货的物流),需人工介入或设计替代方案(如仅退款不退货)
- 🔍 数据隔离性缺失
-
- 脏读风险 :中间状态可能被其他服务读取(如订单显示“已创建”但库存未扣减),需业务层处理(如标记“处理中”状态)
- 🐞 调试与监控困难
-
- 分布式追踪复杂 :需全局事务 ID 串联多服务日志,调试故障需跨系统排查(如支付超时需检查订单、支付、MQ 服务日志)
- ⏳ 长事务资源占用
-
- 业务锁竞争 :如库存预占后未及时释放(需设置 TTL 自动过期),可能导致超卖
推荐使用 Saga 的场景
| 场景类型 | 案例与说明 |
|---|---|
| 跨服务长流程业务 | 电商订单(创建→扣库存→支付→发货),流程步骤多、耗时长 |
| 最终一致性场景 | 旅行预订(机票+酒店+租车),允许分钟级延迟 |
| 异构系统集成 | 第三方支付 + 自建库存系统,无法统一事务管理 |
| 高并发写入 | 秒杀系统库存扣减,需快速释放资源 |
- Saga 更适合串行场景:通过补偿机制处理长事务的最终一致性,适合电商、物流等业务流程。
- TCC 更适合并行场景:通过资源预留和两阶段提交保障强一致性,适合金融、高并发交易。
三段式提交流程
事务消息通过三个阶段协调生产者与消息队列的行为:
- 半消息发送( Half Message )
-
- 生产者发送消息到MQ Broker,但该消息被标记为 “暂不可投递” ****状态(即半消息),存储在特殊Topic(如RMQ_SYS_TRANS_HALF_TOPIC)中,消费者无法感知24。
- 目的 ****:避免本地事务未完成时,下游提前消费导致数据不一致。
- 本地事务执行与二次确认
-
- 生产者收到半消息成功响应后, 执行本地事务 ****(如订单创建、库存扣减)34。
- 根据事务结果向Broker发送指令:
-
- Commit :半消息转为正式消息,投递给消费者。
- Rollback :删除半消息,事务终止15。
- 事务状态回查(补偿机制)
-
- 若生产者未及时发送Commit/Rollback(如宕机),Broker会 主动回查 ****生产者,询问本地事务状态34。
- 生产者需实现回查接口,返回事务结果(Commit/Rollback),确保状态最终确定45。
事务消息
关键技术保障
- 半消息隔离机制
-
- 半消息存储于独立Topic,通过替换原消息的Topic/Queue属性实现消费者不可见,避免脏读5。
- 事务状态持久化与恢复
-
- Broker将半消息和OP消息(标记最终状态)持久化到磁盘事务日志,即使Broker宕机重启后也能恢复状态24。
- 回查策略优化
-
- 超时触发 ****:半消息超过transactionTimeOut(默认60秒)未确认时触发回查4。
- 指数退避重试 ****:回查失败后按指数退避策略(如2ⁿ秒间隔)重试,上限可配置(默认15次)4。
- 消费端幂等性
-
- 因网络重试可能导致消息重复投递,消费者需通过 唯一业务****ID+ 状态校验 ****实现幂等处理(如Redis记录已处理ID)
本地消息表
、核心原理
- 事务原子性绑定
-
- 业务操作(如订单创建)与消息记录(如库存扣减通知)在 同一个本地事务 ****中完成,确保二者同时成功或失败156。
- 示例:创建订单时,同步插入一条状态为“待发送”的消息到本地消息表。
- 异步消息投递
-
- 通过 定时任务 ****扫描消息表中“待发送”的记录,将消息投递到消息队列(如RocketMQ、Kafka)26。
- 发送成功后更新消息状态为“已发送”;失败则递增重试次数,超过阈值标记为“死亡消息”36。
- 消费端幂等性
-
- 消费者需保证多次处理同一消息的结果一致(如通过唯一业务ID去重)56。
- 示例:库存服务通过Redis缓存已处理的订单ID,避免重复扣减。