> For the complete documentation index, see [llms.txt](https://tommyyang.gitbook.io/javainterview/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tommyyang.gitbook.io/javainterview/architecture/2pc-3pc.md).

# 分布式一致性协议

**2PC** 和 **3PC** 协议。

## 2PC - 二阶段提交

2PC，Two-Phase Commit 的缩写，即二阶段提交，是计算机网络尤其是在数据库领域内，为了使分布式下的所有结点在进行事务处理过程中能够保持原子性和一致性的一种算法。通常，二阶段提交协议也被认为是一种一致性协议，用来保证分布式系统的一致性。目前，绝大多数关系型数据库都是采用二阶段提交协议来完成分布式事务处理的，利用该协议能够非常方便地完成所有分布式事务参与者的协调，统一决定事务的提交和回滚，从而能够有效地保证分布式系统的一致性，因此，二阶段提交协议被广泛地应用到许多分布式系统中。

### 协议说明

顾名思义，二阶段提交协议是将事务的提交过程分成了两个阶段来进行处理，其执行流程如下：

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

1. 事务询问

   协调者向所有参与者发送事务内容，询问是否可以执行事务的提交操作，然后等待所有参与者的响应。
2. 执行事务

   各参与者结点执行事务操作，并将 Undo 和 Redo 信息记入事务日志中。
3. 各参与者向协调者反馈事务询问的响应。

   如果参与者成功执行了事务操作，那么就反馈给协调者 YES 响应，表示事务可以执行； 如果参与者没有成功执行事务，那么就反馈给协调者 NO 响应，表示事务不可以执行。

上述阶段类似于一个 "投票阶段"，即各参与者投票表明是否要继续执行接下来的事务提交操作。

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

该阶段根据阶段一中各参与者的反馈情况来决定最终是否可以进行事务提交操作，正常情况下，有如下两种可能。

**执行事务提交**

假如协调者从各参与者那得到的反馈是 YES 响应，那么就会执行事务提交操作。

1. 发送提交请求

   协调者向所有参与者节点发出 Commit 请求。
2. 事务提交

   参与者接收到 Commit 请求后，会正式执行事务提交操作，并在完成提交之后释放在整个 事务执行期间占用的事务资源。
3. 反馈事务提交结果

   参与者在完成事务 Commit 之后，向协调者发送 Ack 消息。
4. 完成事务

   协调者在接收到所有参与者节点反馈的 Ack 消息后，完成事务。

**中断事务**

假如协调者从任意一个参与者那得到了 NO 响应，或者在等待超时之后，协调者尚无法接收到所有 参与者的反馈响应，那么就中断请求。

1. 发送回滚请求

   协调者向所有参与者节点发出 Rollback 请求。
2. 事务回滚

   参与者接收到 Rollback 请求后，会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作， 并在完成回滚之后释放在整个事务执行期间占用的资源。
3. 反馈事务回滚结果

   参与者在完成事务 Rollback 之后，向协调者发送 Ack 消息。
4. 中断事务

   协调者在接收到所有参与者节点反馈的 Ack 消息后，完成事务中断。

总结来说，二阶段提交将一个事务的处理过程分为了投票和执行两个阶段，其核心是对每个事务都采用了先尝试后提交的处理方式，因此也可以将二阶段提交看作是一个强一致性算法，下图 1 和图 2 分别展示了二阶段提交过程中"事务提交"和"事务中断"两种场景下的交互流程。

**事务提交场景：** ![](https://blog.tommyyang.cn/img/architecture/2PC-commit.png)

**事务中断场景：** ![](https://blog.tommyyang.cn/img/architecture/2PC-rollback.png)

### 优缺点

二阶段提交协议：

* 优点：原理简单，实现方便。
* 缺点：同步阻塞、单点问题、脑裂、太过保守。

`同步阻塞`

```
二阶段提交协议存在的最明显也是最大的一个问题就是同步阻塞，这会极大的限制分布式系统的性能。在二阶段提交
的执行过程中，所有参与该事务操作的逻辑都处于阻塞状态，也就是说，各个参与者在等待其它参与者响应的过程中，
也无法进行其它任何操作。
```

`单点问题`

```
协调者在整个二阶段提交协议中起到了非常重要的作用。一旦协调者出现问题，那么整个二阶段提交流程将无法运转，
更为严重的是，如果协调者在第二阶段出现问题，那么其它参与者将会一直处于锁定事务资源的状态中，而无法继续
完成事务操作。
```

`数据不一致`

```
在二阶段协议的阶段二，即执行事务提交的时候，当协调者向所有参与者发送 Commit 请求后，发生了局部网络
异常或协调者在尚未发布完 Commit 请求前自身崩溃了，导致最终只有部分参与者接收到了 Commit 请求。于是，
这部分接收到了 Commit 请求的参与者就会执行事务的提交，没有接收到的就无法进行事务的提交，而是处于等待
过程中，于是整个分布式系统便出现了数据不一致的现象。
```

`太过保守`

```
如果协调者在获取参与者事务是否可以提交的询问过程中，参与者出现故障而导致协调者一直无法获取到所有参与者的
响应，这时，协调者只能根据自身的的超时机制来判断是否中断事务，这样的策略显得比较保守。换句话说，二阶段提
交协议没有设计较为完善的容错机制，任意一个节点的失败都会导致整个事务的失败。
```

## 3PC - 三阶段提交

### 协议说明

3PC，是 Three-Phase Commit 的缩写，即三阶段提交，是 2PC 的改进版，其将二阶段提交协议的"提交事务请求" 过程一分为二，形成了有 CanCommit、 PreCommit 和 do Commit 三个阶段组成的事务处理协议，其协议设计如 下图： ![](https://blog.tommyyang.cn/img/architecture/3PC.png)

#### 阶段一： CanCommit

1. 事务询问

   协调者向所有参与者发送一个包含事务的 canCommit 请求，询问是否可以执行事务提交操作，并开始等待各参与者 的响应。
2. 各参与者向协调者反馈事务询问的响应

   各参与者在接收到协调者的 canCommit 请求后，正常情况下，如果其自身认为可以顺利执行事务，那么反馈 YES 响应，并进入预备状态；否则返回 NO 响应。

#### 阶段二： PreCommit

协调者会根据各参与者的响应来决定是否是否可以进行事务的 preCommit 操作，正常情况下，包含两种结果。

`执行事务提交`

```
所有参与者返回 YES 请求。

1. 发送预提交请求

    协调者向所有参与者节点发出 preCommit 请求，并进入 Prepared 阶段。

2. 事务预提交

    参与者接收到 preCommit 请求后，会执行事务操作，并将 Undo 和 Redo 信息记录到事务日志中。

3. 各参与者向协调者反馈事务执行的响应

    如果参与者成功地执行了事务操作，那么就会反馈给协调者 Ack 响应，同时等待最终的指令：
    提交（`commit`） 或中止（`abort`）。
```

`中断事务`

```
有一个以上的参与者返回 NO 请求，或者在等待超时之后，协调者尚无法接收到所有参与者的响应，那么就会
中断事务。

1. 发送中断请求

    协调者向所有参与者发送 abort 请求。

2. 中断事务

    无论是收到来自协调者的 abort 请求，或者是在等待协调者请求过程中出现超时，参与者都会中断事务。
```

#### 阶段三： DoCommit

该阶段进行真正的事务提交，会存在以下两种可能的情况。

`执行提交`

1. 发送提交请求。

   进入这一阶段，假设协调者处于正常工作状态，并且它接收到了所有参与者的 Ack 响应，那么它将从"预提交"状态转换到"提交"状态， 并向所有参与者发送 doCommit 请求。
2. 事务提交。

   参与这接收到 doCommit 请求后，会正式执行事务提交操作，并在完成提交之后释放在整个事务执行期间占用的事务资源。
3. 反馈事务提交结果。

   参与者在完成事务提交之后，向协调者发送 Ack 消息。
4. 完成事务。

   协调者在接收到所有参与者反馈的 Ack 消息后，完成事务。

`中断事务` 进入这一阶段，假设协调者处于正常工作状态，并且有任意一个参与者向协调者反馈了 NO 响应，或者在等待超时之后，协调者尚无法 接收到所有参与者的反馈响应，那么中断事务。

1. 发送中断请求。

   协调者向所有参与者发送 abort 请求。
2. 事务回滚。

   参与者接收到 abort 请求后，会利用其在阶段二中记录的 Undo 信息来执行事务回滚操作，并在完成回滚之后释放在整个事务 执行期间占用的资源。
3. 反馈事务回滚结果。

   参与者在完成事务回滚之后，向协调者发送 Ack 消息。
4. 中断事务。

   协调者在接收到所有参与者反馈的 Ack 消息后，中断事务。

需要注意的是，一旦进入阶段三，可能会存在以下两种故障。

* 协调者出现问题。
* 协调者和参与者之间的网络出现故障。

  无论出现那种情况，最终都会导致参与者无法及时接收到来自协调者的 doCommit 请求或是 abort 请求，针对这种情况，参与者都会在等待超时之后，继续执行事务提交。

### 优缺点

三阶段提交协议：

* 优点：相较于二阶段提交协议，三阶段提交协议最大的优点就是降低了参与者的阻塞范围，并且能够在出现单点故障后，继续达成数据 一致。
* 缺点：三阶段提交协议在去除阻塞的同时也引入了新的问题，那就是参与者在接收到 preCommit 消息后， 如果网络出现分区 （网络分区指由于网络设备的failure，造成网络分裂为多个独立的组），此时协调者所在的节点和参与者无法进行正常 的网络通信，在这种情况下，该参与者依然后进行事务的提交，这必然会出现数据的不一致。
