前言
本文主要讲述 2pc和3pc在整个流程过程中,如何保证一致性,以及存在的优点和缺点,对2pc和3pc整体流程很不了解的同学可以先查找相关文章了解或者看看这篇介绍https://www.jianshu.com/p/28f1869500fa 。2pc和3pc都是理论模型,所以本文主要围绕理论,具体的实现会列举相近的实现机制供参考。
2pc 角色
coordinator 协调者 participiants 参与者
2pc participant状态
accept,refuse,commit,abort
2pc流程
阶段1:
coordinator ---proposol-->all participants
coordinator <--accept/refuse --all participants
阶段2:
coordinator --commit-->all participants
if any of all participants return refuse ,coordinator --abort--> all participants
宕机恢复
coordinator | participant | |
---|---|---|
情况1 | 正常 | 正常 |
情况2 | 正常 | 有宕机 |
情况3 | 有宕机 | 正常 |
情况4 | 有宕机 | 有宕机 |
情况1
这种情况就是二阶段提交的正常执行流程。
情况2
从coordinator角度只有两种情况,participant在未回复proposol前挂掉,participant未回复commit/abort挂掉。(因为对于coodinator而言,没有收到回执,信息是否到达participant,participant是否处理没有区别)
- 如果coordinator发送proposol未收到回执,可以将此次proposol取消,这个情况下根据策略的不同有两种解决方式。一种可以将宕机机器从coordinator活跃列表里剔出,这样又回到场景1的情况,然后当宕机机器重启后,可以进行事务replication(类似mysq主从复制)。另一种策略要求必须所有的participant都参与,那么必须等待该机器重启。
-
如果coordinator发送commit/abort未收到回执,因为所有的participant都收到了同样的操作指令,commit或者abort,只需要宕机机器回复后查询其它节点,不会产生不一致。
情况3
从coordinator角度看,只有一种情况。对于新产生的coordinator,无论之前的coordinator在何种情况下宕机,新的coordinator都需要从所有participant获取当前状态。因为所有的participant都会保留状态,所以coordinator的决策将不会产生不一致情况。
情况4
新选出的coordinator需要从存活的participant获取各个节点的状态,根据各个节点状态的不同,采取不同决策。
节点组合 | participant节点状态 | coordinator决策 | 宕机节点状态 |
---|---|---|---|
节点状态组合1 | accept,reuse | abort | refuse/abort |
节点状态组合2 | accept, commit | commit | accept/commit |
节点状态组合3 | accept,abort | abort | accept/abort/refuse |
节点状态组合4 | accept | 阻塞 | accept/refuse/commit/abort |
- 除表格中的状态外,还会有节点状态全部为commit,refuse,abort,(refuse,abort)等情况,这些情况,很明确coordinator应该采取什么操作,并且不会发生不一致。
- 表格中1,2,3节点状态组合是一种情况,coordinator可以明确需要采取什么操作。我们拿组合3来详细解释,在存活节点组合的状态分别为accept和abort时,如果宕机节点状态为commit 那么在第一轮投票所有的participant一定都是accept,那么不会有存活节点状态为abort。宕机节点为accept,refuse,abort则满足,有其他节点为abort的情况。这种情况下新的coordinator采取abort操作不会产生不一致,宕机节点回复后,如果时accept,refuse状态,可以查询其他节点状态回归到abort状态。
- 节点组合状态4中,已知所有节点状态都是accept,宕机节点可能为四种状态任一状态,若新coordinator采取commit 或者abort,宕机节点都有可能为相反状态,所以会产生不一致。coordinator必须采取阻塞方式,等待宕机节点恢复,方可知道宕机节点状态采取操作。这是2pc最大的缺点,下文将讲述3pc为什么能避免这个问题。
3pc participant状态
accept,refuse,pre-commit,commit,abort
3pc 流程
阶段1:
coordinator --> proposol --> participant;
coordinator<-- accept/refuse <-- participant;
阶段2:
coordinator --> pre-commit --> participant (if participant ack accept)
coordinator --> abort --> participant (if participant ack refuse)
阶段3:
coordinator --> commit --> participant(all participant at pre-commit )
coordinator --> abort --> participant( when need recover)
宕机恢复
3pc和2pc宕机分类情况一样,情况1,3的机理和2pc大体相近。下面详细解释下情况2,4。
情况2
从coordinator角度有三种情况,participant在未回复proposol前挂掉,participant未回复pre-commit前挂掉,participant未回复commit挂掉。
- 如果coordinator发送proposol未收到回执,可以直接cancel。
- 如果coordinator发送pre-commit/abort未收到回执,可以直接abort,因为不会有任何机器处于commit状态。
- 如果coordinator发送commit/abort未收到回执,因为所有的participant都收到了同样的操作指令,commit或者abort,只需要宕机机器回复后查询其它节点,不会产生不一致。
情况4
新coordinator产生后向各个participant查询各节点状态,各节点可能状态组合如下:
节点组合 | participant节点状态 | coordinator决策 | 宕机节点状态 |
---|---|---|---|
节点状态组合1 | accept,refuse | abort | refuse/abort |
节点状态组合2 | accept, pre-commit | commit | accept/refuse/pre-commit |
节点状态组合3 | accept,abort | abort | accept/abort/refuse |
节点状态组合4 | accept,refuse,abort | abort | accept/abort/refuse |
节点状态组合5 | accept | abort | accept/refuse/pre-commit/abort |
节点状态组合6 | pre-commit,commit | commit | pre-commit/commit |
- 节点组合1,3,4和2pc阶段一致。
- 节点组合2情况下,宕机节点在1阶段回执一定是accept,不可能为refuse否则不可能会有pre-commit,所以coordinator可以放心执行后续的commit操作。
- 节点组合6情况下,宕机节点状态肯定为pre-commit和commit之一,所以coordinator可以放心执行commit操作。
- 节点组合五情况下,宕机节点不可能为commit状态,因为如果宕机节点为commit,那么其余节点,都必须为pre-commit或者commit状态。所以,coordinator可以放心进行abort,不会产生不一致。如果执行commit操作,因为宕机节点可能是abort,所以有可能造成不一致。这也是2pc和3pc重要区别之一,3pc不会存在因为不确定宕机节点状态情况下,必须阻塞等待宕机节点回复。
3pc引申
我们试想是否存在节点状态组合为:accept,pre-commit,commit。如果存在这种情况,那么一个节点到达pre-commit后,将不需要等待其它节点都到达pre-commit状态,可以进行commit。那么在上文3pc节点状态组合5中,宕机节点状态将可能为commit,那么就会造成不一致了。细心的读者肯定发现,上文3pc节点状态组合为:accept,pre-commit,但是coordinator可以执行commit操作。这里其实是,coordinator可以等待所有accept状态节点走到pre-commit状态后,进行commit操作。因为宕机节点为accept和pre-commit操纵所以不会产生不一致。
我们再进一步思考,如果存活的节点都是pre-commit状态,会不会存在不确定宕机节点接收到pre-commit后,回复是否可以commit?那么这种情况下,是否会回到2pc状态下的困局,只能等待宕机节点回复的情况。这就是proposal操作和pre-commit操作的区别,pre-commit只会回应,不会有accept和refuse的区别。coordinator只有收到所有节点accept后,才会发出pre-commit消息,只有收到所有存活节点pre-commit回应后,才会向存活节点发送commit指令。
最后的总结是本文核心,必须反复思考各种情况下的可能,才能够真的理解清楚。