三阶段提交协议(Three-phase Commit Protocol)
In computer networking and databases, the three-phase commit protocol (3PC) is a distributed algorithm which lets all nodes in a distributed system agree to commit a transaction. It is a more failure-resilient refinement of the two-phase commit protocol (2PC).
与两阶段提交不同的是,三阶段提交有两个改动点:
- 引入超时机制 - 同时在 coordinator 和 participant 中都引入超时机制。这保证了 participant 的事务锁在指定时间后会被自动释放(release),从而避免了因锁长期被占用导致查询性能下降的情况
- 把两阶段提交协议的第一个阶段(Voting Phase)拆分成了两个独立的阶段(CanCommit 和 PreCommit),以保证在最后提交阶段之前,各个 participant 的状态是一致的。
三段提交的核心理念是:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。
理论上来说,如果第一阶段所有的结点返回成功,那么有理由相信成功提交的概率很大。这样一来,可以降低参与者Cohorts的状态未知的概率。也就是说,一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了。
所谓的三个阶段分别是:
- 第一阶段 - 询问:CanCommit
- 第二阶段 - 锁定资源(预提交事务):PreCommit
- 第三阶段 - 真正提交:DoCommit
三阶段提交的过程
阶段一 - CanCommit
事务询问
3PC 的 CanCommit 阶段其实和2PC的 Voting Phase 很像,即 coordinator 向 participant 发送 commit
请求,以询问是否可以执行事务提交操作。
但与 2PC 中的 Voting Phase 的区别是, 3PC的 CanCommit 阶段并不真正让 participant 执行“预提交本地事务”(而是检查是否能够正常“预提交本地事务”)。
响应反馈
participant 接到 CanCommit
请求之后,正常情况下,如果其自身认为可以顺利执行“预提交事务”(但不会真正执行预提交),则返回 Yes 响应,并进入预备状态;否则反馈 No。
阶段二 - PreCommit
coordinator 在得到所有 participant 的响应之后,会根据其响应结果执行下面任一一种操作:
- 执行事务预提交
- 中断事务
如果在超时时间后,仍然没有收到所有 participant 的响应,也会中断全局事务。
情况1 - 执行事务预提交
- coordinator 发送预提交请求 - coordinator 向所有 participant 发出
preCommit
的请求,以使得 participant 进入prepared
状态。 - participant 预提交事务 - participant 收到
preCommit
请求后,会提交事务,对应 2PC 准备阶段中的 “执行事务”,也会 Undo 和 Redo 信息记录到本地的事务日志中。 - 各 participant 响应反馈 - 如果这个 participant 成功执行了事务,就反馈 ACK 响应,同时等待指令:提交(commit) 或终止(abort)。
情况2 - 中断事务
- coordinator 向所有 participant 发送 abort 请求或者超时了
- participant 中断事务 - participant 如果收到 abort 请求或者超时,就会中断事务。
阶段三 - DoCommit
该阶段 participant 会真正 commit 本地事务,具体分为以下两种情况:
- commit 本地事务
- abort 本地事务
情况1 - commit 全局事务
如果 coordinator 发送了 commit 请求,且 commit 请求到达了每一个 participant,或者在达到超时时间后,participant 没有收到 coordinator 的 DoCommit(commit 或者 rollback)
- coordinator 发送 commit 请求 - coordinator 在接收到各 participant 发送的 ACK 响应后,他将从预提交状态(PreCommit)进入到提交状态(DoCommit),并向所有 participant 发送
doCommit
请求。 - participant commit 本地事务 - participant 接收到
doCommit
请求之后,commit 本地事务。并在完成事务提交之后释放所有事务资源。 - participant 响应反馈 - participant commit 本地事务之后, participant 向 coordinator 发送 ACK 响应。
- 完成事务 - coordinator 接收到所有 participant 的 ACK 响应之后,完成全局事务。
值得强调的是,在达到超时时间后,participant 没有收到 coordinator 的 DoCommit,3PL 会仍然提交,而不是让 participant 的本地事务继续被阻塞(直到收到 coordinator 发来的 commit)
- 这适当缓解了 participant 的本地事务继续被阻塞的时长和可能性
- 但仍然无法完全避免数据的不一致
情况2 - abort 全局事务
如果 coordinator 收到的 participant 的 ACK 不是 Yes
,就会中断全局事务:
- coordinator 发送 abort请求 - coordinator 向所有 participant 发送
rollback
请求 - participant 回滚事务 - participant 接收到
rollback
请求之后,利用其在PreCommit
阶段记录的 undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源 - participant 反馈结果 - participant 完成事务回滚之后,向 coordinator 发送
ACK
消息。 - coordinator abort 全局事务 - coordinator 接收到每一个 participant 反馈的
ACK
消息之后,完成全局事务的中断。
三阶段提交的优缺点
优点
相对于二阶段提交,三阶段提交主要解决的单点故障问题,并减少了阻塞的时间。
- 具体来说,因为一旦 participant 无法及时收到来自 coordinator 的 DoCommit 信息(commit 或者 rollback),他会默认执行 commit。而不会一直持有事务资源并处于阻塞状态,直到收到DoCommit 信息才结束阻塞。
缺点
三阶段提交也会导致数据一致性问题。由于网络原因, coordinator 发送的 abort 响应没有及时被 participant 接收到,那么 participant 在超时达到之前,都不会执行 rollback 操作:
- 这里可能出现两种情况,如果participant 在超时达到时,仍没有收到 coordinator 发送的 DoCommit 信息,则执行 commit(这点和 2PL 不同);如果 participant 在超时达到之前,收到了 DoCommit 信息,那当然基于信息中的内容来执行
这样就和其他接到 abort 命令并执行回滚的 participant 之间存在数据不一致的情况。
总结来说,三阶段提交中是否会发生 participant 的本地事务被阻塞,依赖于 participant 到这个 participant 的网络的高可用性。而在分布式系统中,网络往往有可能会出问题。因此这个缺点其实是相对于有多个 node 都去通知这个 participant 的情况而言的,在后者的情况中,我们假设多个 node 到这个 participant 的网络都发生故障的概率是更低的(事实上未必一定是这样),因此本地事务资源并处于阻塞状态的时长会相对更短。
Reference
- https://ecommons.cornell.edu/bitstream/handle/1813/6323/82-483.pdf?sequence=1&isAllowed=y
- Wikipedia Three-phase commit protocol - https://en.wikipedia.org/wiki/Three-phase_commit_protocol
- Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲《Transaction Across DataCenter》
- https://coolshell.cn/articles/10910.html
- Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems
- https://juejin.cn/post/6844903609209978887
- https://www.alibabacloud.com/blog/tech-insights---two-phase-commit-protocol-for-distributed-transactions_597326