当前位置: 首页 > news >正文

分布式2PC理论

目录

什么是分布式 2PC(Two-Phase Commit)

2PC 的工作原理

2PC 的优缺点

为什么 2PC 不完全可靠?

超时问题

协调者故障


什么是分布式 2PC(Two-Phase Commit)

定义
        2PC 是一种原子提交协议,用于把一次“跨多个节点/资源管理器”的事务做成要么全部提交、要么全部回滚
参与角色只有两个:
协调者(Coordinator / 事务管理器 TM):发起、收集投票并下达最终决定。
参与者(Participants / RM):各个数据库/服务,执行本地操作并在提交前表态

资源管理器(RM)是管理具体数据资源的组件,即使在同一节点上,不同类型的资源管理器也属于多个资源。常见的资源管理器包括:
数据库
消息队列
缓存
分布式文件系统
例如一个事务需要同时完成
数据库更新订单状态消息队列发送通知消息,这两个操作分别由数据库 RM消息队列 RM管理,属于跨资源管理器的事务。

两阶段握手(简化时序)

  应用││ 1. 执行业务写入(各节点先本地执行但不提交)▼
协调者 ──Prepare──► 参与者1/2/.../N     (阶段一:投票/准备)◄─Yes/No─┆      ◄─Yes/No─┆├─若全部 Yes → ──Commit──► 全部参与者   (阶段二:提交)└─有 No/超时 → ──Rollback► 全部参与者   (阶段二:回滚)

关键点
        参与者在 Prepare 成功时必须把提交所需日志落盘、加锁/冻结相关行,承诺“收到 Commit 就一定能提交、收到 Rollback 就一定能回滚”
        2PC 只负责原子提交的协调,并不替代各节点的并发控制(MVCC/2PL)与隔离级别

为什么要Prepare 阶段落盘 + 加锁/冻结?

当参与者收到协调者的 Prepare 请求时,它要做两件关键事:

1. 写日志(落盘)

在数据库里,这叫 Prepare Log

含义是:我已经执行了本地事务的操作(写入/更新),但是先不提交。

写日志到磁盘是为了保证:即使机器宕机重启,能恢复我当时是准备提交的状态,避免数据丢失。

2. 锁定相关资源

比如要更新一行库存,把它加上锁,保证别的事务不能改。

因为你还没最终决定提交还是回滚,所以必须冻结住,避免数据被其他事务破坏。

3. 承诺的含义

如果后来收到 Commit 指令:一定能提交(因为日志+锁都在)

如果收到 Rollback:一定能回滚(因为我记录了未提交前的状态)

2PC 只负责原子提交协调,不替代并发控制?

2PC 解决的问题:
保证一个跨节点事务,要么所有人都提交,要么所有人都回滚。
(即原子性 + 一致性)

但它不解决的问题:
多个事务并发时的数据冲突。
比如两个事务同时扣库存,一个要减 5,一个要减 10,该谁先执行?会不会导致库存负数?
这种属于并发控制的范畴,需要 MVCC(多版本并发控制)或 2PL(两阶段加锁)等机制。

在分布式事务中的地位
        2PC 是最经典最通用的原子提交基线方案XA 标准就是基于 2PC。MySQL/PostgreSQL/Oracle 等都提供(XA、Prepared Transactions)
        它把跨库/跨服务的一致提交从业务里抽象到事务管理器(TM)层来做,业务方只关心成功/失败
        现代系统里,2PC常作为强一致方案的起点,在需要更高可用/弹性的场景,会引入 3PC、TCC、Saga、Paxos/Raft+两段提交等替代或改进。

什么是原子提交基线方案?
在分布式系统里,最基础最常见最权威的解决原子提交的方法就是 2PC

原子提交 = 所有节点的结果要么全成,要么全败

基线方案 = 社区/学术界/工业界最普遍认可的最低要求的做法

换句话说:
2PC 是跨节点事务一致性的起点和底线
如果连 2PC 都没实现,就不能保证强一致性

XA 标准是啥?

XA 标准由 X/Open 组织提出的一种分布式事务接口规范

它规定了事务管理器(TM)和资源管理器(RM) 之间的接口。

内核就是基于 2PC 协议。

结构:

TM(Transaction Manager):负责全局事务的开始、提交、回滚。

RM(Resource Manager):数据库、消息队列等资源。

XA 定义了 TM 调用 RM 的接口,比如 xa_start, xa_prepare, xa_commit, xa_rollback

所以XA是2PC的工业标准化版本
MySQL、Oracle、DB2 都支持XA接口

XA、Prepared Transactions 是啥?

XA 事务就是遵循 XA 标准的分布式事务。应用调用 TM,TM 协调多个 RM(数据库),执行 2PC 流程。比如 Java EE 里的 JTA(Java Transaction API) 就是 XA 的实现。

Prepared Transactions(预提交事务)

是数据库对 2PC 的支持点。

在 Prepare 阶段,数据库执行事务但不提交,写入 Prepare 日志。

PostgreSQL 就有 PREPARE TRANSACTION 'xid'; 命令

它允许事务在待提交/待回滚的状态下存活,直到协调者发出最终决定

这就是为什么数据库里有 prepared state 这种中间状态既不是 committed,也不是 rolled back。它就是 2PC 的产物

与单机事务的区别

维度单机事务(本地 DB)分布式事务 2PC
参与节点1 个多个 RM
提交决定本地直接 COMMIT/ROLLBACK由协调者收集投票后统一决定
故障模型只考虑本机/本库还要考虑网络分区、消息丢失、任意一方宕机
锁的持有持有到本地提交从 Prepare 开始到最终决议都要持有(更长,易阻塞)
恢复通过 WAL/REDO/UNDO还需要全局事务日志和决议重放
代价一次落盘两轮网络往返 + 多节点落盘,延迟与抖动更大

单机是我自己说了算
2PC 是所有人都说能交卷,我再统一交;有人说不行就全部撤回

2PC 的工作原理

2PC(Two-Phase Commit,两阶段提交)就是:
在一个跨多个节点的事务中,引入一个 协调者(Coordinator),它负责向所有参与者(Participants) 发号施令。整个协议分为两个阶段:
准备阶段(Prepare/Voting Phase)
提交阶段(Commit Phase)
核心思想:先投票,再决策。
参与者要么一致答能提交,要么有任何一方答不行
协调者最后统一下达全部提交全部回滚的命令。

1. 协调者与参与者的角色
协调者(Coordinator)
类似总指挥
负责发起事务、收集各参与者反馈、做出最终决策。
有权决定全体提交或全体回滚。
参与者(Participants)
每个分布式节点,比如数据库分片、库存服务、支付服务。
接到指令时:
准备阶段执行本地事务并锁定资源。
提交阶段根据协调者的决定,真正提交或回滚。

2. 消息交互流程(投票 + 提交)

整个过程的时序图:

应用事务发起│▼
[协调者]  -------------------  发起 Prepare 请求  ------------------► [参与者1..N]◄-------------------- 返回 Yes/No --------------------││ 收集所有投票│├── 如果全部 Yes → 发送 Commit 请求│                         └────────► [参与者1..N 提交事务并解锁]│└── 如果有 No/超时 → 发送 Rollback 请求└────────► [参与者1..N 回滚事务并解锁]

交互过程就是:
投票阶段:协调者问 → 参与者答。
提交阶段:协调者决定 → 参与者执行。

两个阶段的作用

阶段一:准备阶段(Voting Phase / Prepare Phase)
协调者动作:
给所有参与者发出 Prepare 请求。

参与者动作:
执行本地事务操作
写入 Undo Log/Redo Log,并持久化
给相关记录加锁,防止并发修改
回复协调者Yes(我已准备好,可以提交)或 No(执行失败,不能提交)

作用:让所有参与者表态,保证在进入下一步前,大家都能提交

阶段二:提交阶段(Commit Phase)
协调者动作:
收集所有参与者的投票结果
如果全是 Yes → 发 Commit 请求
如果有 No 或超时 → 发 Rollback 请求

参与者动作:
收到 Commit:真正提交事务(把日志刷盘 + 更新数据可见 + 解锁)
收到 Rollback:撤销事务(按 Undo Log 回滚 + 解锁)

作用:做出最终决定,保证所有节点的事务命运一致

2PC 的优缺点

优点(为什么很多系统以 2PC 为起点)

1) 强一致性(原子提交)
保证跨多个资源的要么都成功,要么都失败
发生在同一事务内的多处写入,由协调者统一裁决,避免部分提交的脏状态。
 

2) 模型简单、生态成熟
角色清晰(协调者/参与者)、时序固定(Prepare→Commit/Rollback),实现成本低。
XA 标准等接口广泛可用,落地门槛低。

2PC 是把跨节点的一组写操作变成不可分割的原子动作的通用基线方案

缺点(为什么实际大规模场景常“绕开”或“增强”2PC)

痛点现象成因影响常见缓解
阻塞参与者长时间持锁等待Prepare 后到最终决议之间必须锁定行/范围吞吐下降、延迟陡增、易死锁缩短事务,把耗时 IO 移到事务外;原子语句更新;乐观并发+重试;按键/分片限流
协调者单点协调者宕机后事务悬而未决(in-doubt)2PC 天生非容错,协调者保存全局决议参与者一直持锁或占用“prepared”槽位协调者做共识化(Raft/Paxos)复制日志;恢复/重放;人工清理超时 in-doubt
网络分区/消息丢失有人收到了决议,有人没收到;或一直等不到决议分布式不可靠通信 + 2PC 无法自证全局状态可用性下降(等不到决定就不能推进),甚至运维介入决议落盘后幂等重试广播;参与者定期“拉取决议”;运维回收
性能开销额外的两轮网络 RTT + 两次强制落盘Prepare 要 fsync(承诺点),Commit 也要落盘延迟上升、吞吐降低,抖动放大批量化/合并事务、网络就近、降低事务跨度;“Presumed Abort/Commit”优化日志
运维复杂度“悬而未决事务”排查困难跨库跨机房,状态不一致但持锁业务阻塞、SLA 受损统一事务监控;超时/重放工具;灰度回滚策略
极端下的启发式决策参与者自作主张回滚/提交长超时 + 业务压力导致“heuristic”操作破坏原子性(最坏)严禁启发式提交;只允许回滚启发式并做审计;预案演练

为什么 2PC 不完全可靠?

2PC 在安全性(原子性)上依赖“协调者最终能把同一个决议告知所有参与者”;在可用性上会阻塞。一旦协调者长时间不可达、或网络分区持续存在,系统就会陷入悬而未决(in-doubt)状态;如果采用启发式决策(heuristic),则安全性也不再保证。

1. 协调者宕机 → 事务悬挂(in-doubt)
时序窗口:
    W1:协调者在收集投票前挂掉 → 没人进入 prepared,安全
    W2:所有参与者已返回 YES,协调者尚未把提交决议落盘/广播就挂 → 所有参与者处于                       prepared、加锁,不知道后续如何
    W3:协调者已写入提交决议且广播给部分参与者后挂 → 有的已提交,有的仍在 prepared(等 待决议)
        从最终性看:只要协调者恢复后能补发决议,原子性仍能收敛
        从可用性看:未收到决议的一侧长时间阻塞、占锁、顶资源

2. 网络分区/消息丢失
2PC 假设最终可以把同一决议送达所有参与者。当网络长期分区:
一侧已经提交,另一侧还在 prepared;
对外观测会出现“部分提交、部分未提交”的暂态不一致;
只有当网络恢复、做重放/补发后,才会收敛为一致;期间可用性差。
若人为启发式提交/回滚以“解堵”,则破坏原子性(最坏情况)

3. 阻塞特性是协议内生的
prepared 后,参与者必须持锁直到收到决议,写-写并发会被阻塞;
这不是实现细节,而是 2PC 的承诺点设计决定的(保证“接到 Commit 就一定能提交”)

超时问题

超时 ≠ 可以随便回滚。2PC 的正确做法要分处于哪个阶段

1. 定义
超时:一方在合理时限内没有收到对等方应答(参与者等不到协调者的决议,或协调者等不到参与者的投票)

2. 正确处置(分阶段)
阶段 A:投票前(参与者尚未 prepared)
    协调者可安全地中止事务(Abort),参与者无副作用

阶段 B:参与者已返回 YES(已写入 prepared)
    参与者不得自行提交或回滚(否则破坏原子性)
    正确动作:
        1. 保持 prepared 与锁(可设置上限时间,避免无限占用)
        2. 周期性重试联系协调者
        3. 支持“拉取决议”(用全局事务 ID 向协调者/事务记录表查询最终决定)
        4. 宕机恢复后,从本地prepared 列表中继续追决议

阶段 C:协调者侧超时
    未收到任何参与者的 YES,协调者可以判定失败并发 Rollback
    但一旦发现有人已经 YES,必须先把最终决议落盘(commit/abort),再幂等广播

术语含义执行者场景
commit决议:事务最终执行提交协调者协调者决策阶段(落盘决议)
abort决议:事务最终执行终止协调者协调者决策阶段(落盘决议)
rollback动作:参与者执行 “撤销修改”参与者参与者收到 abort 决议后执行

3. 错误姿势(会破坏原子性)
参与者在 prepared 状态自行回滚或自行提交(所谓 heuristic 决策)
实践中如必须“解堵”,也只允许启发式回滚(保守选择),且需审计与补偿,但依然要明白这已偏离 2PC 的安全模型。

4. “更先进协议”能否避免阻塞?
3PC(三阶段提交)在 2PC 之上加了一个中间状态(pre-commit)和超时可推进机制,理论上减少阻塞;但在异步网络 + 可能分区模型下,3PC 仍存在安全边界问题,工业界很少单独采用。
Paxos/Raft + 两段提交(又称 Paxos Commit)更常见:
决议事务记录写入共识日志,协调者可以无缝切换
参与者可从共识组拉取最终决议,阻塞窗口显著缩小
这并非去掉 2PC,而是把协调者是单点这个薄弱环节用共识加固

协调者故障

1. 问题本质
协调者掌握最终决议的唯一真相;
若它不可达或状态未持久化,参与者就会悬而未决。

2. 改进一:协调者共识化
把协调者的全局决议日志放到 Raft/Paxos:
流程要点:
协调者收齐 YES → 先把提交(或回滚)决议写入共识日志并提交
再向参与者幂等广播该决议
协调者宕机 → 选出新主,凭共识日志里的决议继续补发
参与者重启/超时 → 按事务 ID 拉取决议(而不是干等)

收益:
决议不丢(被多数副本持久化)
协调者不再是单点
in-doubt 时间显著缩短(可用性提升)

3. 改进二:日志恢复 + 决议拉取
协调者恢复:
从共识日志中找出已决定未完播与未决定的事务
对已决定:补发给所有参与者(幂等)
对未决定:根据投票情况与策略统一中止(如 presumed abort)

参与者自救:
启动时扫描本地 prepared 列表
逐个向协调者/事务记录表查询决议并执行
查询不到时按策略退回(只在采用 presumed abort/commit 的系统里,且需与 TM 协议一致)

4. 辅助优化:Presumed Abort / Presumed Commit
Presumed Abort(PA):把默认结局设为回滚(未见到决议就当失败),减少恢复时需要的日志/状态
Presumed Commit(PC):默认提交

http://www.xdnf.cn/news/1374535.html

相关文章:

  • 使用 html2canvas + jspdf 实现页面元素下载为pdf文件
  • UE5 查找组件
  • 云原生安全架构设计与零信任实践
  • 预测模型及超参数:1.传统机器学习:SVR与KNN
  • 工业网络安全:保护制造系统和数据
  • HIVE的Window functions窗口函数【二】
  • 【Hadoop】Zookeeper、HBase、Sqoop
  • 全球位置智能软件CR10为73%,市场集中度高
  • Java中高效获取IP地域信息方案全解析:从入门到生产实践
  • jQuery版EasyUI的ComboBox(下拉列表框)问题
  • JS(面试)
  • Proxmox VE 中启用 CentOS 虚拟机的串口终端(xterm.js 控制台)
  • 深度剖析HTTP和HTTPS
  • .NetCore 接入 Nacos,实现配置中心和服务注册
  • 本地windows电脑部署html网页到互联网:html+node.js+ngrok/natapp
  • oracle 表空间扩容(增加新的数据文件)
  • 使用appium对安卓(使用夜神模拟器)运行自动化测试
  • STM32八大模式
  • 基于单片机空调温度控制测温ds18b20系统Proteus仿真(含全部资料)
  • 人机交互如何变革科普展示?哪些技术正成吸睛焦点?
  • 初春养生指南模板页
  • Rust 登堂 之 迭代器Iterator(三)
  • el-carousel在新增或者删除el-carousel-item时默认跳到第一页的原因和解决
  • betaflight configurator 如何正确烧写飞控
  • 基于muduo库的图床云共享存储项目(二)
  • Linux 云服务器内存不足如何优化
  • 【RAG】使用llamaindex进行RAG开发
  • 6 种无需 iTunes 将照片从 iPhone 传输到电脑
  • TDengine IPv6 支持用户手册
  • “java简单吗?”Java的“简单”与PHP的挑战:编程语言哲学-优雅草卓伊凡