前言
本文主要講述 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 |
這種情況就是二階段送出的正常執行流程。
從coordinator角度隻有兩種情況,participant在未回複proposol前挂掉,participant未回複commit/abort挂掉。(因為對于coodinator而言,沒有收到回執,資訊是否到達participant,participant是否處理沒有差別)
- 如果coordinator發送proposol未收到回執,可以将此次proposol取消,這個情況下根據政策的不同有兩種解決方式。一種可以将當機機器從coordinator活躍清單裡剔出,這樣又回到場景1的情況,然後當當機機器重新開機後,可以進行事務replication(類似mysq主從複制)。另一種政策要求必須所有的participant都參與,那麼必須等待該機器重新開機。
- 如果coordinator發送commit/abort未收到回執,因為所有的participant都收到了同樣的操作指令,commit或者abort,隻需要當機機器回複後查詢其它節點,不會産生不一緻。
從coordinator角度看,隻有一種情況。對于新産生的coordinator,無論之前的coordinator在何種情況下當機,新的coordinator都需要從所有participant擷取目前狀态。因為所有的participant都會保留狀态,是以coordinator的決策将不會産生不一緻情況。
新選出的coordinator需要從存活的participant擷取各個節點的狀态,根據各個節點狀态的不同,采取不同決策。
節點組合 | participant節點狀态 | coordinator決策 | 當機節點狀态 |
---|---|---|---|
節點狀态組合1 | accept,reuse | abort | refuse/abort |
節點狀态組合2 | accept, commit | commit | accept/commit |
節點狀态組合3 | accept,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 流程
coordinator --> proposol --> participant;
coordinator<-- accept/refuse <-- participant;
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。
從coordinator角度有三種情況,participant在未回複proposol前挂掉,participant未回複pre-commit前挂掉,participant未回複commit挂掉。
- 如果coordinator發送proposol未收到回執,可以直接cancel。
- 如果coordinator發送pre-commit/abort未收到回執,可以直接abort,因為不會有任何機器處于commit狀态。
新coordinator産生後向各個participant查詢各節點狀态,各節點可能狀态組合如下:
accept,refuse | |||
accept, pre-commit | accept/refuse/pre-commit | ||
accept,refuse,abort | |||
節點狀态組合5 | accept/refuse/pre-commit/abort | ||
節點狀态組合6 | pre-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指令。
最後的總結是本文核心,必須反複思考各種情況下的可能,才能夠真的了解清楚。