文章目录
看2pc和3pc看的晕晕乎乎的,看了很多博客,感觉说的都不够细致,看起来也容易犯晕,找到了两篇英文文档(不算原文),看起来好像是清楚一些,有些时候这些协议类的东西研读,如果不是那种特别长的建议直接阅读原文,或者原文阅读作为辅助,可以更快的全面了解,这一篇就算是英文文档的翻译吧。
1. two phase commit protocol
两阶段提交协议是一种分布式算法,它使分布式系统中的所有站点都同意提交事务。该协议保证即使在站点故障和消息丢失的情况下,所有节点都能统一保持提交或abort事务。
但是,该协议将不能处理多个随机站点同时故障(超过一个)的情况。该算法的两个阶段分为COMMIT-REQUEST阶段和COMIT阶段,在COMMIT-REQUEST阶段,coordinator尝试让所有的cohorts完成准备,在COMMIT阶段,coordinator完成所有cohorts的事务。
1.假设前提
该协议以以下假设方式工作:
- 将一个节点指定为coordinator,即主站点,将网络中的其余节点称为cohorts(同类)。
- 每个站点的存储都是稳定的以及每个节点都使用的预写日志(先写日志再执行操作)。
- 该协议还假设没有节点永久崩溃,并且最终任何两个节点都可以相互通信。后者并不算十分苛刻,因为通常可以重新路由网络通信。前者是一个非常强的假设(想想假设机器炸毁了!)
很多博客在介绍2pc的时候这些前提假设都完全没有提
2. 算法概述
在阶段1期间
- coordinator发送commit-request 给所有cohorts。然后,它等待所有cohorts的返回。对于cohorts来说,如果事务执行成功,则该cohort将一个条目写入undo-log,并将一个条目写入redo-log。
- 然后,cohorts回复一个一个agree的信息,如果事务在cohorts节点执行失败,则返回abort信息。
在阶段2期间
- 如果coordinator从所有cohorts接收到同意消息,则它将提交记录写入其日志中,并向所有cohorts发送commit消息。
- 如果并不是所有的cohorts回复的都是agree消息,则coordinator将发送abort消息。
- 接下来,coordinator等待cohorts的确认。当收到来自所有cohorts的ack时,coordinator会将完整的记录写入其日志。注意,coordinator将永远等待直到所有的ack返回。
- 如果cohorts收到的是commit消息,它将释放在事务处理期间持有的所有锁和资源,并将ack消息发送给协调者。如果收到的是abort消息,则将使用undo-log撤消该事务,并释放在事务期间持有的资源和锁。然后发送一个ack消息。
上面的描述实际上还不算非常清晰,只能算是一个大概的描述,后面会更清晰的描述这个过程。
3. 缺点
-
两阶段提交协议的最大缺点是它是阻塞协议。节点在等待消息时将阻塞。这意味着其他进程将不得不等待被阻塞进程持有的锁资源释放。即使所有其他站点都发生故障,单个节点也将继续等待。如果coordinator永久失败,则某些cohorts将永远无法解决其事务,这会导致永久占用锁资源。
-
另一个缺点是该协议过于保守。它更关注abort的情况而不是agree的情况(不管有多少个agree,有一个abort就不行)
附加一些自己的理解,这里的过于保守会导致两个主要的问题
1.因为某个节点不行导致事务无法提交
2.因为某个节点比较慢导致短板效应,每个事务的处理都会很慢,整个系统的吞吐量上不去
4. 详情
这里更加详细的介绍一下2pc理论的过程
1. coordinator 端来看
- coordinator将prepare消息发送到每个cohort, coordinator现在处于准备事务状态
- 现在,coordinator正在等待每个cohort的响应。如果任何cohort响应ABORT,则必须abort该事务,会执行步骤5。如果所有cohorts都同意,则可以提交该事务,继续执行步骤3。如果过了一段时间,所有cohorts没有响应,那么coordinator可以发送向所有cohort发送abort消息,或将COMMIT-REQUEST消息发送给尚未响应的cohort。无论哪种情况,coordinator最终都将进入状态3或状态5。
- 在日志中记录一个COMPLETE,以指示事务现在正在进行complete阶段。然后向每个cohorts发送COMMIT消息。
- 等待每个cohorts做出回应。他们必须回复COMMIT。如果一段时间之后,某些cohort没有响应,则重新发送COMMIT消息。一旦所有cohorts均已回复,从永久存储器中擦除所有执行这次事务的相关辅助型信息。完成。
- 向每个 cohort 发送abort信息.
2. cohorts端
- 如果在COHORT上收到某个事务t的COMMIT-REQUEST消息,该消息在COHORT中是无法被执行的,回复ABORT。反之,将事务的新状态写入永久存储中的UNDO和REDO日志中。这样的话就可以完成恢复旧状态(在以后被abort的情况下)或commit,而不用担心崩溃。事务的读取锁可能在此时被释放;但是,写锁仍然需要保留。然后发送agree 给coordinator。
- 如果收到ABORT消息,则终止该事务,利用undo-log恢复到事务发生之前的状态。
- 如果收到COMMIT消息,则说明该事务已准备好提交或已经提交。如果已准备好,则执行更新数据库并释放事务拥有的其余锁。如果已经提交,则无需采取进一步措施。回复给coordinator。
5. 正确性分析
我们声称,如果一个cohort完成了事务,那么所有cohort最终都会完成事务。
正确性的证明有些非正式:
如果一个COHORT正在完成一个事务,那是因为coordinateor向它发送了COMMT消息。仅当coordinator处于提交阶段时才发送此消息,在这种情况下,所有COHORTS均已响应coordinateor AGREED。这意味着所有cohorts均已准备好事务,这意味着此时任何崩溃都不会损害事务数据,因为它的redo/undo log位于永久存储中。coordinator完成后,在删除coordinator的数据之前,会确保确保每个corhort都完成了。因此,coordinateor的崩溃不会影响结果。
6. 简单总结
这里有几个比较重要的点
- 一个是几个假设
- 还有就是coordinator在第二个阶段进行commit的时候会等待所有节点的ack回来才结束,要不然就会一直重试的发送commit信息。
- 因为采用的都是先记录log后操作的方式( write ahead log),所以不用担心宕机可能导致数据丢失。
- 在开头说的不2pc协议不会用来处理超过一个节点宕机的描述还不是很清楚,感觉好像多节点宕机也没有问题的样子。
- 同样的,在这里coordiantor宕机后不会选新的,而是等待他fail-over之后继续担当coordinator。
- 很多博客在介绍2pc的时候完全不提假设,而且,交互过程完全是随心推论,很多地方都不严谨,看的我云里雾里的,大家有精力还是翻翻英文文档吧,我这里可能有些地方翻译的也不一定对,文末有英文原文出处链接。
- 整体来看,2pc对外部环境要求非常严格,而且容易产生阻塞,所以在生产环境中直接使用2pc作为分布式一致性实现的系统几乎没有,但是很多系统有优化后的实现,比如微软的PacificA协议的数据复制阶段就是在2pc上加一改造,引入了一个ISR队列来改善了阻塞的问题。