# 二阶段提交（2PC）

> 引入一个作为**协调者（coordinator）**&#x7684;组件来统一掌控所有**参与者（participant）**&#x7684;操作结果，并最终指示这些节点是否要把操作结果进行真正的提交。

## 协议说明

<mark style="color:blue;">**二阶段提交协议（Two Phase Commit，2PC）将事务的提交过程分成了两个阶段来进行处理**</mark>，其执行流程如下。

### 阶段一：提交事务请求

1. **事务询问**。协调者向所有的参与者发送事务内容，询问是否可以执行事务提交操作，并开始等待各参与者的响应。
2. **执行事务**。各参与者节点执行事务操作，并将 **Undo** 和 **Redo** 信息记入事务日志中。
3. **各参与者向协调者反馈事务询问的响应**。
   * 如果参与者成功执行了事务操作，那么就反馈给协调者 Yes 响应，表示事务可以执行；
   * 如果参与者没有成功执行事务，那么就反馈给协调者 No 响应，表示事务不可以执行。

### 阶段二：执行事务提交

**在阶段二中，协调者会根据各参与者的反馈情况来决定最终是否可以进行事务提交操作**，正常情况下，包含以下两种可能：

* **执行事务提交：**&#x5047;如协调者从**所有的参与者**获得的反馈**都是** Yes 响应，那么就会执行事务提交。
  1. **发送提交请求**。协调者向所有参与者节点发出 Commit 请求。
  2. **事务提交**。参与者接收到 Commit 请求后，会正式执行事务提交操作，并在完成提交之后释放在整个事务执行期间占用的事务资源。
  3. **反馈事务提交结果**。参与者在完成事务提交之后，向协调者发送 Ack 消息。
  4. **完成事务**。协调者接收到所有参与者反馈的 Ack 消息后，完成事务。
* **中断事务：**&#x5047;如**任何一个参与者向协调者反馈了 No 响应**，或者在**等待超时**之后，协调者尚无法接收到所有参与者的反馈响应，那么就会中断事务。
  1. **发送回滚请求**。协调者向所有参与者节点发出 Rollback 请求。
  2. **事务回滚**。参与者接收到 Rollback 请求后，会利用其在阶段一中记录的 **Undo** 信息来执行事务回滚操作，并在完成回滚之后释放在整个事务执行期间占用的资源。
  3. **反馈事务回滚结果**。参与者在完成事务回滚之后，向协调者发送 Ack 消息。
  4. **中断事务**。协调者接收到所有参与者反馈的 Ack 消息后，完成事务中断。

## 优缺点

二阶段提交将一个事务的处理过程分为了**投票**和**执行**两个阶段，其**核心是对每个事务都采用先尝试后提交的处理方式**。

二阶段提交协议的**优点**：**原理简单，实现方便**。

二阶段提交协议的缺点：

* <mark style="color:blue;">**同步阻塞：**</mark>二阶段提交协议存在的最明显也是最大的一个问题就是**同步阻塞**，这**会极大地限制分布式系统的性能**。**在二阶段提交的执行过程中，所有参与该事务操作的逻辑都处于阻塞状态**，也就是说，各个参与者在等待其他参与者响应的过程中，将无法进行其他任何操作。
* <mark style="color:blue;">**单点问题：**</mark>协调者的角色在整个二阶段提交协议中起到了非常重要的作用。**一旦协调者出现问题，那么整个二阶段提交流程将无法运转**，更为严重的是，**如果协调者是在阶段二中出现问题的话，那么其他参与者将会一直处于锁定事务资源的状态中，而无法继续完成事务操作**。
* <mark style="color:blue;">**数据不一致：**</mark>在二阶段提交协议的**阶段二**，即执行事务提交的时候，当协调者向所有的参与者发送 Commit 请求之后，发生了局部网络异常或者是协调者在尚未发送完 Commit 请求之前自身发生了崩溃，导致**最终只有部分参与者收到了 Commit 请求**。于是，这部分**收到了 Commit 请求的参与者就会进行事务的提交**，而**其他没有收到 Commit 请求的参与者则无法进行事务提交**，于是整个分布式系统便出现了**数据不一致**性现象。
* <mark style="color:blue;">**太过保守：**</mark>如果在协调者指示参与者进行事务提交询问的过程中，参与者出现故障而导致协调者始终无法获取到所有参与者的响应信息的话，这时协调者只能依靠其自身的超时机制来判断是否需要中断事务，这样的策略显得比较保守。换句话说，**二阶段提交协议没有设计较为完善的容错机制，任意一个节点的失败都会导致整个事务的失败。**
