两阶段提交协议(2PC,Two-Phase Commit)

271 阅读5分钟

两阶段提交协议(2PC,Two-Phase Commit) 是分布式事务中确保事务原子性的核心协议,通过协调多个参与节点(资源管理器,RM)实现“全成功”或“全失败”的提交结果。以下是其详细解析:

2PC 的核心目标

原子性:确保事务在所有参与节点上要么全部提交,要么全部回滚。 • 一致性:避免部分节点提交导致数据不一致。


2PC 的角色

  1. 协调者(Coordinator) : 即事务管理器(TM),负责发起和协调事务提交流程。
  2. 参与者(Participant) : 即资源管理器(RM),负责执行本地事务并反馈状态。

2PC 的工作流程

阶段一:准备阶段(Prepare Phase)

  1. 协调者发起请求 • 协调者向所有参与者发送 Prepare 请求,附带全局事务 ID(XID)。
  2. 参与者执行本地事务 • 每个参与者执行本地事务(写入日志、锁定资源),但不提交。 • 参与者将事务状态持久化到日志(用于故障恢复)。
  3. 参与者反馈状态 • 参与者向协调者返回状态: ◦ Yes:本地事务可提交。 ◦ No:本地事务无法提交(如违反约束、死锁)。

阶段二:提交/回滚阶段(Commit/Rollback Phase)

  1. 协调者决策 • 若所有参与者均返回 Yes,协调者发送 Commit 指令。 • 若有任一参与者返回 No,协调者发送 Rollback 指令。
  2. 参与者执行指令 • 收到 Commit:提交本地事务,释放资源锁。 • 收到 Rollback:回滚本地事务,释放资源锁。
  3. 参与者确认完成 • 参与者向协调者发送 Ack(确认完成)。 • 协调者在收到所有 Ack 后结束事务。

image-20250401222349602

image-20250401222452811

image-20250401222511109

image-20250401222525240


2PC 的优缺点

优点

  1. 强一致性:严格保证所有节点的事务原子性。
  2. 简单易理解:流程清晰,适合传统数据库系统。

缺点

  1. 同步阻塞 • 所有参与者在 Prepare 阶段后需等待协调者指令,期间资源被锁定,导致高并发下性能下降。
  2. 单点故障 • 协调者宕机会导致事务阻塞: ◦ 协调者在阶段一宕机:参与者无法得知决策,事务悬挂。 ◦ 协调者在阶段二宕机:参与者可能处于未提交状态,需等待协调者恢复。
  3. 数据锁定时间长 • 资源在 Prepare 阶段后即被锁定,直到事务提交或回滚,易引发死锁或长事务。
  4. 协调者故障恢复复杂 • 需依赖日志和超时机制恢复悬挂事务,实现复杂度高。

2PC 的容错机制

  1. 超时机制 • 参与者等待协调者指令超时后,可主动查询协调者状态或触发回滚。
  2. 日志持久化 • 协调者和参与者在每个阶段记录日志,用于故障后恢复事务状态。
  3. 人工干预 • 在极端情况下(如协调者永久故障),需人工介入处理悬挂事务。

2PC 的实际应用

  1. 传统数据库系统 • 如 MySQL 的 XA 事务(需 InnoDB 引擎支持)、Oracle 的分布式事务。
  2. 跨库事务 • 银行转账场景:账户 A(库1)向账户 B(库2)转账,需保证两库同时成功或失败。
  3. XA 协议的基础 • 2PC 是 XA 协议的核心实现机制。

2PC 的变种与优化

  1. 三阶段提交(3PC) • 在 2PC 的 PrepareCommit 之间插入 Pre-Commit 阶段,减少阻塞时间。 • 引入超时机制,参与者超时后自动提交或回滚,降低单点故障风险。 • 缺点:复杂度更高,且无法完全解决数据不一致问题。
  2. 并行 2PC • 协调者并行发送 PrepareCommit 请求,减少同步等待时间。

2PC 的替代方案

由于 2PC 的性能和可用性缺陷,现代分布式系统常用以下方案:

  1. Saga 模式 • 通过补偿事务(Compensating Transaction)实现最终一致性。 • 示例:订单创建后扣减库存,若后续步骤失败,则触发库存回补。
  2. TCC(Try-Confirm-Cancel) • 业务层分三阶段实现事务: ◦ Try:预留资源(如冻结库存)。 ◦ Confirm:确认操作(正式扣减库存)。 ◦ Cancel:取消操作(释放冻结的库存)。
  3. 基于消息的最终一致性 • 使用消息队列(如 Kafka)异步通知各服务,结合本地事务表保证消息可靠传递。

2PC 的代码示例(伪代码)

# 协调者逻辑
def coordinator(participants):
    # 阶段一:Prepare
    all_yes = True
    for p in participants:
        if not p.prepare():
            all_yes = False
            break
    
    # 阶段二:Commit 或 Rollback
    if all_yes:
        for p in participants:
            p.commit()
    else:
        for p in participants:
            p.rollback()
​
# 参与者逻辑
class Participant:
    def prepare(self):
        try:
            # 执行本地事务(不提交)
            self.execute_local_transaction()
            self.write_log()  # 持久化事务日志
            return True
        except Exception:
            return False
    
    def commit(self):
        self.final_commit()  # 提交事务
        self.release_lock()  # 释放资源锁
    
    def rollback(self):
        self.undo_transaction()  # 回滚事务
        self.release_lock()

总结

2PC 是分布式事务中实现原子性的经典协议,但其同步阻塞、单点故障等问题限制了其在高并发、高可用场景中的应用。在实际系统中,需根据业务需求选择合适方案: • 强一致性要求高:可接受性能损耗的场景(如金融系统),使用 2PC 或 XA 协议。 • 高并发与最终一致性:采用 Saga、TCC 或消息队列等柔性事务方案。