分布式理论系列(二)一致性算法: 2PC 到 3PC 有哪些改进

1,099 阅读8分钟

一、分布式一致性概念

1. 协调者统一调度

在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。在关系型数据库中,由于存在事务机制,可以保证每个独立节点上的数据操作满足ACID。但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况,所以在分布式的场景下,如果不添加额外的机制,多个节点之间理论上无法达到一致的状态。

2PC 和 3PC 提交协议都是引入了一个“协调者 [Coordinator]”的组件来统一调度所有分布式节点的执行(这些被调度的节点被称为“参与者 [Participants]”),让当前节点知道其他节点的任务执行状态,通过通知和表决的方式,决定执行 Commit 还是 Rollback 操作。

2. 一致性算法

两类一致性算法:
2PC 3PC:操作原子性
协议用于保证属于多个数据分片上的操作的原子性。这些数据分片可能分布在不同的服务器上,2PC协议保证多台服务器上的操作要么全部成功,要么全部失败。
Paxos Raft Zab:副本一致性
协议用于保证同一个数据分片的多个副本之间的数据一致性。当这些副本分布到不同的数据中心时,这个需求尤其强烈。

在分布式事务中,2PC 和 3PC 是经典的一致性算法,那么 2PC 和 3PC 的具体流程是怎样的,3PC 又是如何改进的呢?

二、2PC(2 Phase Commit)

两阶段提交中的两个阶段,指的是 Commit-request 阶段和 Commit 阶段,两阶段提交的流程如下:

1. 协议说明

第一阶段:提交事务请求

  1. 事务询问
    协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

  2. 执行事务
    各参与者节点执行事务操作,并将 Undo 和 Redo 信息计入事务日志中。

  3. 反馈响应
    如果本地事务执行成功,反馈给协调者 Yes;
    如果本地事务执行故障,反馈给协调者 No。

第二阶段:执行事务提交

如果把阶段一比喻成投票阶段,那么在阶段二中,就会根据阶段一的投票结果来决定最终是否可以进行事务提交操作。正常情况下,包含两种操作可能:提交事务、中断事务。

  • 提交事务

    如果所有参与者的反馈都是 Yes 响应,那么就会执行事务提交:
    1. 发送提交请求
      协调者向所有参与者节点发出 Commit 请求
    2. 事务提交
      参与者接收到 Commit 请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源
    3. 反馈响应
      参与者在完成事务提交之后,向协调者发送 ACK 信息
    4. 完成事务
      协调者接收到所有参与者反馈的 ACK 消息后,完成事务
  • 中断事务

    任何一个参与者反馈了 No 响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。
    1. 发送回滚请求
      协调者向所有参与者节点发出 Rollback 请求
    2. 事务回滚
      参与者接收到 Rollback 请求后,会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放整个事务执行期间占用的资源
    3. 反馈响应
      参与者在完成事务回滚之后,向协调者发送 ACK 信息
    4. 中断事务
      协调者接收到所有参与者反馈的 ACK 信息后,完成事务中断

2. 存在的问题

(1)同步阻塞

同步阻塞会极大地限制分布式系统的性能。在二阶段提交的执行过程中,所有参与节点都是事务独占状态,当参与者占有公共资源时,那么第三方节点访问公共资源会被阻塞。

(2)单点故障

一旦协调者发生故障,参与者会一直阻塞下去。

(3)数据不一致

在第二阶段中,假设协调者发出了事务 Commit 的通知,但是由于发生了局部网络异常或协调者在尚未发完 Commit 通知之前自身发生了崩溃,导致最终只有部分参与者接收到了 Commit 通知,其余的参与者没有收到通知,一直处于阻塞状态,于是整个分布式系统出现了数据不一致性现象。

(4)太过保守

如果参与者在与协调者通信期间出现故障,协调者只能靠超时机制来判断是否需要中断事务,这个策略比较保守,需要更为完善的容错机制,任意一个节点的失败都会导致整个事务的失败。

三、3PC(3 Phase Commit)

为了解决二阶段协议中的同步阻塞等问题,三阶段提交协议在协调者和参与者中都引入了超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。

三阶段中的 Three Phase 分别为 CanCommit、PreCommit、DoCommit 阶段。

1、协议说明

第一阶段:CanCommit

3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 Commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。

第二阶段:PreCommit

协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。根据响应情况,有以下两种可能。

  • 提交事务

    假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会进行事务的预执行:

    1. 发送预提交请求
      协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段;
    2. 事务预提交
      参与者接收到 PreCommit 请求后,会执行事务操作;
    3. 响应反馈
      如果参与者成功执行了事事务操作,则返回 ACK 响应,同时开始等待最终指令。
  • 中断事务

    1. 发送中断请求
      协调者向所有参与者发送 abort 请求
    2. 中断事务 无论是收到来自协调者的 abort 请求,还是等待超时,参与者都中断事务

第三阶段:DoCommit

该阶段进行真正的事务提交,也可以分为以下三种情况。

  • 执行提交

    1. 发送提交请求
      协调者接收到参与者发送的ACK响应后,那么它将从预提交状态进入到提交状态,并向所有参与者发送doCommit请求。
    2. 事务提交
      参与者接收到doCommit请求之后,执行正式的事务提交,并在完成事务提交之后释放占有的事务资源。
    3. 响应反馈
      事务提交完之后,向协调者发送 ACK 响应。
    4. 完成事务
      协调者接收到所有参与者的 ACK 响应之后,完成事务。
  • 中断事务

    假设协调者正常工作,并且有任一参与者反馈 No,或者在等待超时后无法接收所有参与者的反馈,都会中断事务。

  • 超时提交

    参与者如果没有收到协调者的通知,超时之后会执行 Commit 操作。

2. 存在的问题

三阶段提交协议同样存在问题,具体表现为,如果参与者接收到了PreCommit消息后,出现了不能与协调者正常通信的问题,在这种情况下,阶段三中参与者依然会进行事务的提交,这就出现了数据的不一致性。

3. 优点与改进

  • 引入超时机制

    在 2PC 中,只有协调者拥有超时机制,如果在一定时间内没有收到参与者的消息则默认失败,3PC 同时在协调者和参与者中都引入超时机制。

  • 添加预提交阶段(preCommit)

    在2PC的准备阶段和提交阶段之间,插入一个准备阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段,PreCommit是一个缓冲,保证了在最后提交阶段之前各参数节点的状态是一致的。

四、2PC 和 3PC 的应用

2PC 是一种比较精简的一致性算法/协议,很多关系型数据库都是采用 2PC 协议来完成分布式事务处理的,典型的比如 MySQL 的 XA 规范。

在事务处理、数据库和计算机网络中,2PC 协议提供了分布式设计中的数据一致性的保障,整个事务的参与者要么一致性全部提交成功,要么全部回滚。MySQL Cluster内部数据的同步就是用的 2PC 协议

1. MySQL 的主从复制

在 MySQL 中,二进制日志是 server 层,主要用来做主从复制和即时点恢复时使用的;而事务日志(Redo Log)是 InnoDB 存储引擎层,用来保证事务安全的。

在数据库运行中,需要保证 Binlog 和 Redo Log 的一致性,如果顺序不一致, 则意味着 Master-Slave 可能不一致。

在开启 Binlog 后,如何保证 Binlog 和 InnoDB redo 日志的一致性呢?MySQL 使用的就是 2 PC,内部会自动将普通事务当做一个 XA 事务(内部分布式事务)来处理:

  • Commit 会被自动的分成 Prepare 和 Commit 两个阶段;
  • Binlog 会被当做事务协调者(Transaction Coordinator),BinlogEvent 会被当做协调者日志。

五、参考来源

分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab
拉钩专栏:分布式技术原理与实战45讲