news 2026/8/11 12:59:43

分布式每日一学 — Day 4:分布式事务(2PC / 3PC / Saga / TCC)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式每日一学 — Day 4:分布式事务(2PC / 3PC / Saga / TCC)

🎓 分布式每日一学 — Day 4:分布式事务(2PC / 3PC / Saga / TCC)

前 3 天我们打的是「副本一致性」这条线:CAP 告诉我们 P 没得选,Raft/Paxos 告诉我们"怎么让一组机器对同一份数据达成一致"。
今天我们换一个战场——「跨多个服务/数据库做完一整件事」怎么保证不出错。一个下单链路要改订单库、库存库、账户库、积分库,任何一个环节挂了都不能让用户的钱"消失"或"凭空多出来"。这就是分布式事务的领域。


📝 昨日思考题解答(Day 3 — Paxos)

题目回顾:5 节点 Paxos 集群 A/B/C/D/E。P1(在 A 上)发起Prepare(1),A/B/C 回 Promise 且未接受过提案 → P1 准备Accept(1, V1=100)时 A 宕机重启 → P2(在 D 上)发起Prepare(2),B/C/D/E 回 Promise → B 报告"收到过 Accept(1, V1=100) 但还没回复就被打断"。

Q1:P2 应该选什么 V 提交?

答案:P2 必须选 V = 100。

推导过程

  1. B 是否真正「接受」了 Accept(1, V1=100)?
    Paxos 中"接受"(accept)指的是 Acceptor 收到 Accept 请求后、检查编号 ≥ 自己承诺的最小编号 → 记录该提案。题目说 B “收到过 Accept 但还没回复”——只要 B 已经记录了(1, V1=100),就视为已接受,无论 ACK 是否发出去。

  2. B 在 Promise(2) 时报告了什么?
    Acceptor 的 Promise 必须附带"自己已接受的最高编号提案"。所以 B 在回复 P2 的Prepare(2)时,会报告:已接受 (1, V1=100)

  3. Paxos Phase 2 的值选择规则
    Proposer 收到多数 Promise 后:

    • 如果有 Acceptor 报告已接受提案 → 选编号最大的那个已接受值
    • 如果没人报告 → 用自己的值

    这里 B 报告了(1, V1=100),所以P2 即使本来想提别的值,也必须改成 V=100

🧠这是 Paxos 安全性(Safety)的核心:一旦某个值被多数 Acceptor 接受(或即将被接受),后续 Proposer 就无法再改变它。哪怕 P2 是"后来者",也必须"服从"已有的决议。

Q2:后续如果 P1 醒来再发起 Accept(1, V1) 会发生什么?

答案:Accept(1) 会被拒绝,P1 必须用更高编号重新发起。

推导过程

时间线回顾: P1 的 Accept(1) 发出时 A 宕机了 之后 B/C 已经 Promise(2) → 承诺不再接受编号 < 2 的提案 P1 醒来后发 Accept(1, V1=100): ┌─────────────────────────────────────────────┐ │ B: 拒绝 ❌(已 Promise(2),1 < 2) │ │ C: 拒绝 ❌(已 Promise(2),1 < 2) │ │ D: 拒绝 ❌(已 Promise(2),1 < 2) │ │ E: 拒绝 ❌(已 Promise(2),1 < 2) │ │ A: 可能接受 ✅(重启后磁盘保留 Promise(1)) │ └─────────────────────────────────────────────┘ P1 最多只能拿到 A 的 1 票 → 不够多数 → Accept 失败

P1 的出路:必须用更高编号(如Prepare(3))重新发起提案。但即使 P1 用编号 3 重新跑,Phase 1 时 B/C/D/E 中只要有人报告"已接受 (2, V=100)",P1 就必须继续选 V=100

📌结论:值 100 已经"锁定"在系统中,任何后续提案都无法改变它。这就是 Paxos 保证的“一旦值被选定,就永远不变”

关键启示

Paxos 规则本题体现
Acceptor Promise 后不再接受更低编号提案B/C 拒绝 P1 的 Accept(1)
Proposer 必须采用已接受的最高编号值P2 被迫选 V=100
安全性优先于活性(Safety > Liveness)值锁定后不可逆,但 P1 可能需要重试(活锁风险)

一、为什么需要分布式事务?单机 ACID 不够用了吗?

我们先复习一下单库事务的"四大法宝"(ACID):

属性含义
A — Atomicity(原子性)要么全做、要么全不做
C — Consistency(一致性)事务前后数据满足约束
I — Isolation(隔离性)并发事务互不干扰
D — Durability(持久性)提交后数据不丢

这套机制在一个数据库内部很完美。但只要业务开始"拆开"——分库分表、微服务化、跨库 Join、跨服务调用——单库 ACID 就罩不住了。

🔥 一个真实例子:电商下单

用户下单,链路里要碰 5 个东西: 1. 订单服务 → 写订单表 (order_db) 2. 库存服务 → 扣库存 (stock_db) 3. 账户服务 → 扣余额 (pay_db) 4. 积分服务 → 加积分 (point_db) 5. 消息服务 → 发短信 (msg_service)

如果做到第 3 步账户服务挂了、库存已经扣了,订单也已经写了——用户的钱付了,但货没下成,平台要背锅。

💡 这就是分布式事务要解决的核心问题:让一组跨资源/跨服务的写操作,要么全部成功,要么全部"看起来没发生过"


二、方案 1:两阶段提交(2PC,Two-Phase Commit)

这是最古老、最直接的思路,来自于 1970 年代的数据库理论。

🎬 角色

角色职责
协调者(Coordinator)老大哥,负责问所有人"准备好了没"和最后拍板"提交/回滚"
参与者(Participants)各个数据库/服务,执行具体写操作

🎬 两个阶段

协调者 参与者们 │ │ ┌────────────┼────────────┐ │ │ ▼ │ │ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ │ │ 库存DB │ │ 订单DB │ │ 账户DB │ │ └───┬───┘ └───┬───┘ └───┬───┘ │ │ │ │ │ └────────────┼────────────┘ │ │ │ ═══════════════════════════════════════════════════ 阶段 1 ── Prepare(准备) ═══════════════════════════════════════════════════ │ ▼ 协调者问所有人: "可以提交吗?" ┌────────────┼────────────┐ │ │ │ ▼ ▼ ▼ 库存 订单 账户 上锁+写undo 上锁+写undo 上锁+写undo 回 Yes 回 Yes 回 No❌ │ ═══════════════════════════════════════════════════ 阶段 2 ── Commit / Rollback(根据投票结果拍板) ═══════════════════════════════════════════════════ │ 全 Yes → 协调者发 "Commit" → 各方真正写入 有 No → 协调者发 "Rollback" → 各方回滚到 undo

🔴 2PC 的四大致命问题

问题解释
同步阻塞整段时间内所有参与者都持有锁和资源,其他请求被卡死
协调者单点协调者宕机 = 整批参与者永远等"第二阶段"指令,被锁死
数据不一致协调者发完 Commit 后自己宕机,部分参与者收到、部分没收到 → 状态分裂
不幂等网络重发同一个 Commit,可能触发重复写

🧠想想为什么 2PC 看起来很像 Raft/Paxos,却没那么强?
因为 Raft/Paxos 解决的是"同一份数据谁说了算",而 2PC 是"多个独立资源要不要一起动"。它是投票没错,但承诺是"重"的——一旦 Prepare 成功就锁住资源,没有 Paxos 那样的"编号单调性"兜底。


三、方案 2:三阶段提交(3PC)

理论界觉得 2PC 太脆,于是加了阶段:

Phase 1 ── CanCommit 协调者问:"大家能提交吗?"(不锁资源) Phase 2 ── PreCommit 参与者写 undo log,回复 ACK,正式待命 Phase 3 ── DoCommit 协调者发 commit,参与者提交

🆚 3PC 相对 2PC 的改进

  • 多了一次"预问"减少盲目锁资源
  • 参与者超时机制:超时未收到 DoCommit 就默认提交(避免被锁死)
  • 引入超时心跳之后,协调者宕机参与者也能自己推下去

🪦 为什么工程上几乎不用 3PC?

原因说明
协调者宕机还是无解3PC 的"超时自提交"在网络分区下反而会导致脑裂——一部分人 commit、一部分 rollback
多一轮 RTT 太贵性能严重下降
实现复杂度↑边界条件更多

📌 业界真实状态:3PC 是个"教学价值高、工程价值低"的方案。MySQL、Oracle、PG 的 XA 实现都是 2PC 系。


四、方案 3:Saga 模式(微服务时代的宠儿)

1987 年由 Hector Garcia-Molina 提出,被 Saga Service、Apache ServiceComb Seata 等广泛使用。

🎬 核心思想:长事务拆成 T + 补偿 C

把一笔大事务拆成若干子事务,每个子事务有自己的"反向补偿操作":

主流程: T1 → T2 → T3 → T4 → ✅ 补偿流程: ← C1 ← C2 ← C3 ← C4 ↑ T1 失败 → 走 C2.5 补偿 T2,再走 C1 补偿 T1

例如订餐链路的 Saga 拆解:

子事务正向 T补偿 C
订单创建T1: 写入订单C1: 撤销订单
库存扣减T2: 减库存数C2: 加回库存
扣款T3: 账户减余额C3: 加回余额
发短信T4: 通知用户C4: 发撤回短信

🤹 两种 Saga 协调方式

方式 A:编曲式 / Choreography(事件驱动)
  • 每个 T 完成就发 MQ 事件,下游订阅
  • 没有中心协调器
  • 优点:解耦、简单
  • 缺点:链路难追踪、容易"循环依赖"
方式 B:编排式 / Orchestration(中心化)
  • 有一个 Saga Coordinator,按顺序调用 T1→T2→…
  • 类似工作流引擎
  • 优点:链路可视化、易调试
  • 缺点:协调器成了关键路径

⚠️ Saga 的几个"硬骨头"

  1. 隔离性弱:T2 已扣库存但 T3 失败时,库存被别人看到"已经少"了,但最终要补回去——中间窗口期是脏读。
  2. 必须幂等:补偿可能被重试,每个 Ti / Ci 都要能经得起重复执行。
  3. 补偿不一定能"完全反向":比如"发短信"发出去没法撤回,只能"再发一条说明"。这是补偿 ≠ 反向事务的关键区别。

五、方案 4:TCC(Try-Confirm-Cancel)

支付宝的蚂蚁金服、阿里 Seata 主推的模式,比 Saga 早但工程侵入性更强。

🎬 三阶段

阶段含义关键
Try资源预留:扣库存时只把"可用 → 冻结",不真正减必须可逆
Confirm真正提交:冻结 → 扣减,全部 Try 成功后调用必须幂等
Cancel释放预留:把"冻结 → 可用"还回去必须幂等
所有 Try 全部成功 → 批量 Confirm(这一步通常异步化) 任一 Try 失败 → 调用已成功的 Try 对应的 Cancel

🆚 Saga vs TCC

维度SagaTCC
一致性强度最终一致接近强一致
业务侵入性低,只加补偿方法高,每个业务要写 Try/Confirm/Cancel 三套
资源占用不锁资源Try 阶段长期占着"冻结"额
性能一般(多一次 Reserve)
适用场景长链路 + 跨多服务短链路 + 对一致性要求高(支付类)

📌 选型口诀:链长、轻业务 → Saga;链短、重资金 → TCC


六、其他常见"野路子"方案(实战高频)

除了上面四个"学院派",工程上更常用的是消息中间件派的最终一致方案:

🟢 本地消息表(经典老炮)

主流程: 异步: ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ 业务表写入 │ │ 消息表写入 │ ──轮询──>│ 投递到 MQ │ └──────────┘ └──────────┘ └──────────────┘ └─ 同一事务,一致 ✔ ↓ 下游消费 MQ,处理业务 失败 → 重试 N 次 → 人工介入

关键点:业务表和消息表写在同一个本地事务,靠后台 polling 把消息从 MySQL 推到 MQ。简单、好理解、几乎人人用过

🟢 事务消息(RocketMQ 5.0+ 一类)

  • 生产者发送 Half 消息到 MQ,MQ 不投递
  • 本地事务完成 → 二阶段回调告诉 MQ 是 commit / rollback
  • 回查机制:MQ 定期问生产者"刚才那个消息你还活着吗?"避免悬挂

🟢 最大努力通知(跨公司用)

  • 不要求强一致,只要求"尽量通知到"
  • 典型场景:第三方支付回调、退款通知、运营商充值回调
  • 配重试 + 幂等 + 对账补偿

七、大对照表(建议截图保存)

方案一致性性能可用性业务侵入复杂度典型场景
2PC(XA)强一致❌ 差❌ 协调者单点单库多库、强一致短事务
3PC强一致❌ 很差⚠️几乎不用
Saga最终一致✅ 好✅ 高中(写补偿)长链路、跨多服务、订单
TCC接近强一致⚠️ 一般✅ 高高(三套方法)支付、资金类短链
本地消息表最终一致✅ 好✅ 高99% 的常规业务异步解耦
事务消息最终一致✅ 好✅ 高不希望有 polling 的升级版
最大努力通知弱一致✅ 好✅ 高极低跨公司回调、第三方通知

八、实战选型决策树

Q1: 是不是要求强一致 + 数据量不大? ├─ 是 → 2PC (XA),例如 Seata-XA └─ 否 ↓ Q2: 链路上每个动作都能很方便定义补偿吗? ├─ 不能 → TCC(强制业务做预留) └─ 能 ↓ Q3: 链路长不长(>3 个子事务)? ├─ 长 → Saga(编排式用 Camunda/Cadence/Seata Saga) └─ 短 → Saga 或 本地消息表 都行 Q4: 跨公司吗? └─ 是 → 最大努力通知 + 对账 + 幂等

💡实战真相:80% 的分布式事务场景用「本地消息表 + MQ」就能搞定,剩下 18% 用 Saga,最后 2% 才是 TCC/2PC 战场。别一上来就上 TCC,复杂度的代价远超想象。


九、和前 3 天的连接

之前的知识点和分布式事务的关系
CAP 理论(Day 1)分布式事务本质是在 P 必然存在下选 C 或 A:2PC 偏 C,Saga 偏 A
Raft(Day 2)共识算法是 2PC / Saga 协调器的"心跳保活"基础
Paxos(Day 3)Seata-Server 集群内部就用了类 RAFT/Paxos 来保持协调器高可用

也就是说:分布式事务 = 业务一致性协议 + 共识协议 + MQ。三块拼起来,才是一套完整方案。


十、课堂思考题

🧠 想象一条真实的"转账链路":

用户 A 给用户 B 转账 100 元,要走:
① 风控服务(检查是不是洗钱)→
② 账户服务(A -100,B +100)→
③ 账本服务(写流水)→
④ 积分服务(A +10 积分)→
⑤ 通知服务(短信/站内信)。

问题 1:你会用 Saga 还是 TCC?为什么?

问题 2:如果用 Saga,② “A -100、B +100” 这个子事务,补偿动作具体怎么设计?要注意什么边界条件?

问题 3:如果 MQ 通知用户短信发送失败,重试到第 3 次还是失败,你怎么办?这条业务流算"成功"还是"失败"?

(提示:风控/账本可重;账户主操作要幂等;积分是"福利";通知是"尽最大努力"。)


一句话带走

CAP 告诉你分布式是有代价的,Raft/Paxos 告诉你副本怎么共识,而分布式事务告诉你"怎么把跨服务的多个写操作缝合成一件要么全成要么全不成的事"。80% 的场景用本地消息表 + MQ 就够了;剩下 20% 才是 Saga 和 TCC 的战场。

明天预告:Day 5 — 分布式锁(Redlock、Redisson、ZooKeeper 锁、Chubby 锁),以及"为什么我说你不能完全相信 Redis 的 SETNX?"

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/11 12:58:39

Java教学互动系统开发指南:SSM框架实战与毕业设计要点

1. 项目背景与核心价值 这个Java教学互动系统项目是典型的计算机专业毕业设计选题&#xff0c;也是目前教育信息化领域的热门开发方向。去年帮学弟评审毕业设计时&#xff0c;我发现超过30%的计算机相关专业学生都会选择教学管理系统这类课题。这类项目之所以受欢迎&#xff0c…

作者头像 李华
网站建设 2026/8/11 12:57:26

2026社交辅助机器人医疗康复场景应用:智能机器人推动全球医疗服务模式升级

随着全球人口老龄化加速、慢性疾病负担持续增加以及医疗服务需求不断升级&#xff0c;传统医疗模式正面临人力资源不足、康复效率有限以及个性化服务能力不足等挑战。社交辅助机器人作为融合人工智能、人机交互、机器人控制及医疗康复技术的新兴设备&#xff0c;正在成为智慧医…

作者头像 李华
网站建设 2026/8/11 12:55:22

Apache PLC4X架构设计:工业物联网设备统一接入的最佳实践方案

Apache PLC4X架构设计&#xff1a;工业物联网设备统一接入的最佳实践方案 【免费下载链接】plc4x PLC4X The Industrial IoT adapter 项目地址: https://gitcode.com/gh_mirrors/pl/plc4x Apache PLC4X作为工业物联网领域的关键基础设施&#xff0c;通过统一抽象层设计&…

作者头像 李华
网站建设 2026/8/11 12:54:18

文档协作平台架构设计与性能优化实践

1. 项目背景与核心价值 去年团队内部进行了一次文档协作效率调研&#xff0c;发现一个令人震惊的数据&#xff1a;平均每位成员每周要花费3.5小时在不同文档工具间切换、格式转换和版本核对上。这促使我开始构思"文档便利店"——一个集轻量化写作、智能协作和即时发布…

作者头像 李华
网站建设 2026/8/11 12:52:58

AI助力学术论文投稿:智能期刊匹配与优化指南

1. 期刊投稿困境与破局之道 作为一名在学术圈摸爬滚打多年的研究者&#xff0c;我深知论文被拒的痛苦。记得我博士期间投出的第一篇论文&#xff0c;连续被五家期刊拒稿&#xff0c;那种挫败感至今难忘。如今&#xff0c;随着AI技术的快速发展&#xff0c;我们终于有了更智能的…

作者头像 李华
网站建设 2026/8/11 12:51:53

Path of Building终极指南:打造完美流放之路角色的完整解决方案

Path of Building终极指南&#xff1a;打造完美流放之路角色的完整解决方案 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding Path of Building Community Fork是一款专为《…

作者头像 李华