三阶段提交如何避免阻塞?

Posted

技术标签:

【中文标题】三阶段提交如何避免阻塞?【英文标题】:How does three-phase commit avoid blocking? 【发布时间】:2014-02-20 21:28:34 【问题描述】:

我想了解three-phase commit 如何避免阻塞

考虑以下两种失败场景:

场景 1:在第 2 阶段,协调器向所有群组发送 preCommit 消息,并且已从群组 A 之外的所有群组获得确认。网络问题阻止群​​组 A 接收协调器的 preCommit 消息。队列 A 等待 preCommit 消息超时并选择中止。然后协调者和队列 A 都崩溃了。

场景 2:协议到达阶段 3。协调器向群组 A 发送一个 doCommit 消息。但在它可以发送更多 doCommit 消息之前,协调器崩溃了。群组 A 提交其事务部分然后崩溃。

据我所知,其余群组在情景 1 和情景 2 结束时具有完全相同的状态。因此,当恢复协调员介入时,它如何从剩余群组中找出我们是否处于情景 1 和中止,或者我们处于场景 2 并提交,从而避免阻塞?

【问题讨论】:

这个问题似乎离题了,因为它在程序员的堆栈交换上可能更好。 @Abizern 这个问题是一个实用的、可回答的问题,对于软件开发来说是独一无二的。 【参考方案1】:

三阶段提交并不神奇;它比两阶段提交更具弹性。特别是,3PC 对单点故障具有弹性,但不是所有类型的多点故障。问题中的两种情况都提出了两点故障。换句话说,问题的前提是错误的;它对 3PC 的要求超过了它的能力。

为了进一步阅读,以下是一篇关于该主题的论文摘要中的一句话,Analysis and Verification of Two-Phase Commit & Three-Phase Commit Protocols, by Muhammad Atif,以激发您的兴趣:

我们还将我们的方法应用于其“修改”的变体三相 提交协议(3PC)并证明它是错误的同时 网站故障

我发现这篇论文为文献提供了一个立足点。如果您想深入研究,这方面的内容并不多。

【讨论】:

@eh9 :看了论文等资源;并且仍然不完全清楚为什么协调器失败时 2PC 会阻塞?是不是因为 2PC 中的队列没有使用超时概念? 当协调节点也是投票者时,可能会发生阻塞。假设第一阶段已经完成:所有投票者都已将投票发送给协调者,非协调者节点已投票提交。现在让我们想象一下当协调节点故障停止(崩溃)时会发生什么。一个非阻塞共识算法现在必须在没有这个节点的情况下进行,但它不能,因为它无法区分以下情况:coordinator-voter 已提交(因此每个人都应该提交),或 coordinator-voter 已提交回滚(因此每个人都应该回滚)。【参考方案2】:

在两阶段提交中,协调器向所有参与者(节点)发送准备消息并等待他们的回答。然后,协调员将他们的答案发送到所有其他站点。在提交或中止事务之前,每个参与者都会等待协调者的这些回答。

两阶段提交协议也有限制,因为它是blocking protocol。例如,参与者在等待来自协调者的消息时会阻塞资源进程。如果由于任何原因失败,参与者将继续等待并且可能永远不会解决其事务。因此,资源可能会被无限期地阻塞。另一方面,协调者也会在等待参与者回复时阻塞资源。在这种情况下,协调者也可以阻止 如果没有收到参与者的确认,肯定是这样。

但是,三阶段协议引入了称为pre-commit 的第三阶段。这样做的目的是“消除已提交并正在等待来自协调器的全局中止或提交消息的参与者的不确定期。

When receiving a pre-commit message, participants know that all others have voted to commit. 
If a pre-commit message has not been received the participant will abort and release any blocked resources. 

【讨论】:

【参考方案3】:

在场景 1 中:

在恢复期间: 除 A 外,所有群组都将处于 PRECOMMIT 状态。这告诉恢复节点所有群组都已投票支持该提交并继续前进。所以 A 和 Coordinator 应该处于 PRECOMMIT 状态。由于这是一个非最终状态,因此交易被中止。

在场景 2 中:

在恢复期间: 除 A 外,所有群组都将处于 PRECOMMIT 状态。这告诉恢复节点所有群组都已投票支持提交并继续前进。但由于 A 收到 doCommit 消息,它处于 COMMITTED 状态。如果没有发生崩溃,恢复将要求所有群组提交,因为至少有一个群组已经提交.由于发生崩溃(A 崩溃),恢复节点没有看到具有已提交状态的实时队列,因此它推断没有队列收到 doCommit 消息。因此,事务将被中止,并且将要求所有群组释放资源。

当 A 从崩溃中返回并开始恢复时,它会发现所有其他队列都中止了事务,它也将中止事务。

(来源:swturner at regal.csep.umflint.edu)

【讨论】:

我不相信方案 2 的描述是准确的。协调器应该持久记录它向队列 A 发送了 doCommit 消息。通常,不可能从 PRECOMMIT 状态进入 ABORT 状态(参见图表)。如果队列 A 在提交后恢复并注意到所有其他队列都已中止,则系统将处于不一致状态,因为队列 A 无法撤消提交。【参考方案4】:

帮助我理解非阻塞属性的事情是意识到在第一轮消息之后,两个协议基本上处于相同的状态;所有参与者都同意他们可以提交并正在等待确认。

现在,考虑一下参与者在第一轮回答“ok to commit”后知道什么。

2PC:另一个参与者可能已经收到要提交的消息并已提交,或者,没有参与者可能没有进行任何提交。因此,如果参与者没有听到任何声音,则它不知道小组决定是什么。 3PC:参与者可以确定没有其他参与者执行任何提交操作。在协调器失败的情况下,回滚整个操作仍然是安全的。

继续前进,在 3PC 中的第二轮消息和确认之后,我们保证所有参与者都知道小组决定是提交。

这意味着在 3PC 中,永远不会有参与者执行另一个参与者没有预料到的提交操作。

【讨论】:

以上是关于三阶段提交如何避免阻塞?的主要内容,如果未能解决你的问题,请参考以下文章

分布式事务- 三阶段协议

避免与 Future 阻塞 发生死锁

Java面试题架构篇分布式事务

对分布式的掌握是必须的

Zookeeper - 简述分布式一致性协议(2pc3pcpaxoszab)

当作者试图进入写锁时如何避免阻塞 ReaderWriterLockSlim 读者