74

我相信大多数人都知道什么是 2PC(两阶段提交协议)以及如何在 Java 或大多数现代语言中使用它。基本上,当您有 2 个或更多数据库时,它用于确保事务同步。

假设我有两个 DB(A 和 B)在两个不同的位置使用 2PC。在 A 和 B 准备好提交事务之前,两个 DB 都会向事务管理器报告它们已准备好提交。因此,当事务管理器被确认时,它会向 A 和 B 发送一个信号,告诉他们继续。

这是我的问题:假设 A 收到信号并提交了事务。一切搞定后,B也准备照做,但有人拔掉了电源线,导致整个服务器关机。当 B 重新上线时,B 会做什么?B是怎么做到的?

请记住,A 已提交但 B 未提交,并且我们正在使用 2PC(所以,2PC 的设计停止工作,不是吗?)

4

3 回答 3

83

两阶段提交

两阶段提交并不能保证分布式事务不会失败,但它确实保证它不会在 TM 不知道的情况下静默失败。

为了让 B 将事务报告为准备好提交,B 必须将事务保存在持久存储中(即 B 必须能够保证事务在所有情况下都可以提交)。在这种情况下,B 已将事务持久化,但事务管理器尚未收到来自 B 的确认 B 已完成提交的消息。

当 B 重新联机并要求它提交事务时,事务管理器将再次轮询 B。如果 B 已经提交了事务,它将报告事务已提交。如果 B 还没有提交事务,它将提交,因为它已经持久化了它,因此仍然可以提交事务。

为了使 B 在这种情况下发生故障,它必须经历一次丢失数据或日志条目的灾难性故障。事务管理器仍然会知道 B 没有报告成功的提交。1

在实践中,如果 B 不能再提交事务,则意味着将 B 带出的灾难导致数据丢失,并且当 TM 要求它提交它不知道的 TxID 时,B 会报告错误或没想到处于可提交状态。

因此,两阶段提交并不能防止灾难性故障的发生,但它确实可以防止故障被忽视。在这种情况下,如果 B 无法提交,事务管理器将向应用程序报告错误。

应用程序仍然必须能够从错误中恢复,但事务不能在应用程序不知道不一致状态的情况下静默失败。

语义

  • 如果资源管理器或网络在阶段 1 出现故障,事务管理器将检测到致命错误(无法连接到资源管理器)并将子事务标记为失败。当网络恢复正常时,它将中止所有参与资源管理器的事务。

  • 如果资源管理器或网络在第 2 阶段出现故障,事务管理器将继续轮询资源管理器,直到它恢复。当它重新连接回资源管理器时,它将告诉 RM 提交事务。如果 RM 返回“未知 TxID”的错误,TM 将意识到 RM 中存在数据丢失问题。

  • 如果 TM 在第 1 阶段出现故障,则客户端将阻塞,直到 TM 恢复正常,除非由于网络连接中断而超时或收到错误消息。在这种情况下,客户端会意识到错误并可以重试或自行启动中止。

  • 如果 TM 在第 2 阶段出现故障,那么它将阻止客户端,直到 TM 恢复正常。它已经将事务报告为可提交,并且不应向客户端显示致命错误,尽管它可能会阻塞直到 TM 恢复。TM 仍将事务处于未提交状态,并在事务恢复时轮询 RM 以提交。

资源管理器中的提交后数据丢失事件不由事务管理器处理,并且是 RM 弹性的函数。

两阶段提交并不能保证容错 - 请参阅Paxos以获得解决容错的协议示例 - 但它确实保证分布式事务的部分故障不会被忽视。

  1. 请注意,这种故障也可能会丢失以前提交的事务中的数据。两阶段提交不能保证资源管理器不会丢失或损坏数据,或者 DR 过程不会搞砸。
于 2011-09-12T14:39:09.680 回答
4

我相信三阶段提交是一种更好的方法。不幸的是,我还没有找到任何人实施这种技术。

http://the-paper-trail.org/blog/consensus-protocols-three-phase-commit/

以下是上述文章的基本部分:

2PC 的根本困难在于,一旦协调者做出提交决定并与某些副本进行通信,这些副本就会继续执行提交语句,而无需检查是否每个其他副本都收到了消息。然后,如果提交的副本与协调者一起崩溃,则系统无法告知事务的结果是什么(因为只有协调者和收到消息的副本才能确定)。由于事务可能已经在崩溃的副本上提交,因此协议不能悲观地中止——因为事务可能具有无法撤消的副作用。同样,协议不能乐观地强制事务提交,因为最初的投票可能是中止。

这个问题——主要是——通过向 2PC 添加一个额外的阶段来规避,不出所料地为我们提供了一个三阶段提交协议。这个想法很简单。我们将 2PC 的第二阶段——“提交”——分成两个子阶段。第一个是“准备提交”阶段。当协调者在第一阶段收到一致的“是”票时,它会将此消息发送给所有副本。收到此消息后,副本进入一种能够提交事务的状态——通过获取必要的锁等——但至关重要的是,它们不会做任何他们以后无法撤消的工作。然后他们回复协调员,告诉它“准备提交”消息已收到。

此阶段的目的是将投票结果传达给每个副本,以便无论哪个副本死亡,都可以恢复协议的状态。

协议的最后阶段与 2PC 中最初的“提交或中止”阶段几乎完全相同。如果协调者收到来自所有副本的“准备提交”消息的传递确认,则可以安全地继续提交事务。但是,如果未确认交付,则协调者无法保证协议状态在崩溃时会恢复(如果您要容忍固定数量的失败 f,则协调者可以在收到 f+1 后继续确认)。在这种情况下,协调者将中止事务。

如果协调器在任何时候崩溃,恢复节点可以接管事务并从任何剩余的副本中查询状态。如果已提交事务的副本崩溃,我们知道每个其他副本都收到了“准备提交”消息(否则协调器不会移动到提交阶段),因此恢复节点将是能够确定事务能够被提交,并安全地引导协议完成。如果任何副本向恢复节点报告它尚未收到“准备提交”,则恢复节点将知道该事务尚未在任何副本上提交,因此将能够悲观地中止或重新运行协议从一开始就。

那么3PC能解决我们所有的问题吗?不完全是,但它很接近。在网络分区的情况下,轮子会脱落——想象所有收到“准备提交”的副本都在分区的一侧,而那些没有收到的副本在另一侧。然后两个分区将继续与分别提交或中止事务的恢复节点,并且当网络合并时系统将具有不一致的状态。所以 3PC 和 2PC 一样具有潜在的不安全运行,但总会取得进展并因此满足其活性特性。3PC 不会阻塞单节点故障这一事实使其对高可用性比低延迟更重要的服务更具吸引力。

于 2015-03-31T13:18:24.557 回答
3

尽管付出了所有努力,但您的情况并不是唯一可能最终出错的情况。假设 A 和 B 都向 TM 报告了“准备提交”,然后有人拔掉了 TM 和 B 之间的界限。B 正在等待 TM 的允许(或禁止),但它肯定赢了不要一直等待直到 TM 重新连接(由于显而易见的原因,它自己在事务中涉及的资源必须在整个等待时间内保持锁定/不可访问)。因此,当 B 为自己的口味等待太久时,它将采取所谓的“启发式决策”。也就是说,它将决定独立于 TM 提交或回滚,基于,嗯,我真的不知道是什么,但这并不重要。很明显,任何此类启发式决策都可能偏离 TM 做出的实际提交决策。

于 2015-03-31T13:36:21.567 回答