两阶段提交
我相信大多数人知道什么是2PC(两阶段提交协议),以及如何在Java或大多数现代语言中使用它。 基本上,它是用来确保交易是同步的,当你有2个或更多的数据库。
假设我有两个DB(A和B)在两个不同的位置使用2PC。 在A和B准备提交事务之前,两个数据库都会向事务pipe理器报告,说他们已经准备好提交了。 所以当事务pipe理器被确认时,它会发回一个信号给A和B,告诉他们继续。
这是我的问题:比方说,A收到信号,并进行交易。 一旦一切完成,B即将做同样的事情,但有人拔掉电源线,导致整个服务器关机。 当B重新上线时,B会做什么? B怎么做?
请记住,A是承诺,但B不是,我们正在使用2PC(所以,2PC的devise停止工作,不是吗?)
两阶段提交
两阶段提交并不能保证分布式事务不能失败,但是它确实保证不会在TM没有意识到的情况下自动失败。
为了让B将事务报告为准备提交,B必须拥有持久存储中的事务(即B必须能够保证事务可以在所有情况下提交)。 在这种情况下,B一直坚持交易,但是交易经理还没有收到B的消息,确认B已经完成提交。
当B再次联机并要求它提交事务时,事务pipe理器将再次轮询B. 如果B已经提交了交易,它将报告交易已经提交。 如果B还没有提交交易,则它将继续进行交易,因为它已经坚持交易,因此仍然可以进行交易。
为了使B在这种情况下失败,它将不得不经历一场灾难性的失败,失去了数据或日志条目。 交易pipe理者仍然知道B没有报告成功的提交。 1
在实践中,如果B不能再提交事务,就意味着发生了B的灾难导致了数据丢失,而当TM要求提交它不知道的TxID时,B会报告错误,没想到是处于一个可以承受的状态。
因此,两阶段提交并不能防止发生灾难性失败,但它确实防止了失败被忽视。 在这种情况下,如果B不能提交,事务pipe理器将向应用程序报告错误。
应用程序仍然必须能够从错误中恢复,但事务不会在应用程序不知晓不一致状态的情况下以静默方式失败。
语义
-
如果资源pipe理器或networking在阶段1中出现故障,则事务pipe理器将检测到致命错误(无法连接到资源pipe理器)并将子事务标记为失败。 当networking恢复时,它将中止所有参与资源pipe理器上的事务。
-
如果资源pipe理器或networking在阶段2中出现故障,则事务pipe理器将继续轮询资源pipe理器,直到其恢复。 当它重新连接回资源pipe理器时,它会告诉RM提交事务。 如果RM按照“未知的TxID”返回错误,则TM将知道RM中存在数据丢失问题。
-
如果TM在阶段1中停止,则客户端将阻塞,直到TM恢复,除非超时或由于断开的networking连接而接收到错误。 在这种情况下,客户端意识到错误,可以重新尝试或启动中止本身。
-
如果TM在第二阶段下降,那么它将阻止客户端,直到TM恢复。 它已经将交易报告为可承诺的,并且不应该向客户呈现致命的错误,尽pipe它可能会阻塞,直到TM回来。 TM将仍然处于未提交状态的事务处理,并将在事务回来时轮询RM进行提交。
资源pipe理器中的提交后数据丢失事件不由事务pipe理器处理,并且是RM的弹性的function。
两阶段提交并不能保证容错能力 – 请参阅Paxos ,其中提供了解决容错问题的协议示例 – 但它确实保证了分布式事务的部分故障不会不被注意到。
- 请注意,这种失败也可能会丢失以前提交的事务的数据。 两阶段提交并不保证资源pipe理器不能丢失或损坏数据,或者DR过程不会搞砸。
你的情况不是唯一的一个事情,尽pipe所有的努力最终会出错。 假设A和B都向TM报告了“准备提交”,然后有人拔掉了TM和B之间的界限.B正在等待TM的反转(或不反转),但它肯定赢了在TM重新连接之前不会一直等待(由于显而易见的原因,在整个等待时间内,涉及交易的资源必须保持locking/不可访问)。 所以当B等待自己的口味时间过长时,将采取所谓的“启发式决策”。 也就是说,它会决定独立于TM来提交或回滚,呃,我不知道是什么,但这并不重要。 很明显,任何这样的启发式决策都可能偏离TM所做出的实际承诺决定。
我相信三阶段提交是一个更好的方法。 不幸的是,我还没有发现任何人实施这样的技术。
http://the-paper-trail.org/blog/consensus-protocols-three-phase-commit/
以下是上述文章的主要部分:
2PC的根本困难在于,一旦协调者作出决定并传达给一些复制品,复制品就会直接向前行动,而不会检查每个其他复制品是否得到该消息。 然后,如果提交的副本与协调者一起崩溃,则系统无法告诉交易的结果(因为只有协调员和获得该消息的副本确实知道)。 由于事务可能已经在崩溃的副本上被提交,协议不能悲观地中止 – 因为事务可能具有不可能撤消的副作用。 同样,议定书也不能乐观地迫使交易实施,因为最初的投票可能是放弃的。
这个问题大部分是通过向2PC添加一个额外的阶段来避免的,毫不奇怪,我们给了我们一个三阶段的提交协议。 这个想法很简单。 我们打破了2PC的第二阶段 – “承诺” – 分成两个子阶段。 首先是“准备提交”阶段。 当协调员在第一阶段收到一致的“是”票时,协调员将这个信息发送给所有复制品。 收到这个消息后,副本进入一个他们可以提交事务的状态 – 通过获取必要的锁等等,但最重要的是不做任何他们以后不能撤销的工作。 然后,他们回复协调员,告诉它收到了“准备提交”的信息。
此阶段的目的是将投票结果传达给每个副本,以便无论哪个副本死亡都可以恢复协议的状态。
协议的最后阶段与2PC中的原始“提交或放弃”阶段几乎完全相同。 如果协调员收到来自所有副本的“准备提交”消息的确认,则继续进行交易是安全的。 但是,如果交付没有得到确认,协调员就不能保证协议状态如果能够崩溃就能被恢复(如果你容忍固定数量的故障,协调员一旦接收到f + 1就可以继续确认)。 在这种情况下,协调员将中止交易。
如果协调器在任何时候都会崩溃,恢复节点可以接pipe事务并从任何剩余的副本中查询状态。 如果已提交事务的副本已经崩溃,则我们知道每个其他副本都收到了“准备提交”消息(否则协调器将不会移动到提交阶段),因此恢复节点将能够确定交易是否能够被执行,并安全地将协议放在最后。 如果任何副本向恢复节点报告尚未收到“准备提交”的副本,恢复节点将知道该事务没有在任何副本上提交,因此能够悲观地中止或重新运行该协议从一开始就。
3PC也解决了我们所有的问题吗? 不完全,但接近。 在networking分区的情况下,轮子相当松动 – 想象所有收到“准备提交”的副本位于分区的一侧,而那些不在另一侧的副本。 然后这两个分区将继续与分别提交或中止事务的恢复节点,并且当networking合并时,系统将具有不一致的状态。 所以3PC和2PC一样有可能不安全的运行,但总是会有进步,因此满足它的活跃性。 3PC不会阻塞单节点故障的事实使得它对于高可用性比低等待时间更重要的服务更具吸引力。