爲了解決分佈式系統的一致性問題,在長期的探索研究過程當中,涌現出了一大批經典的一致性協議和算法,其中最著名的就是二階段提交協議、三階段提交協議和Paxos算法。算法
在分佈式系統中,每個機器節點雖然都可以明確地知道本身在進行事務操做過程當中的結果是成功或失敗,但卻沒法直接獲取到其餘分佈式節點的操做結果。所以,當一個事務操做須要跨越多個分佈式節點的時候,爲了保持事務處理的ACID特性,就須要引入一個稱爲」協調者「的組件來統一調度全部分佈式節點的執行邏輯,這些被調度的分佈式節點則被稱爲「參與者」。協調者負責調度參與者的行爲,並最終決定這些參與者是否要把事務真正進行提交。基於這個思想,衍生出了二階段提交和三階段提交兩種協議。數據庫
2PC,是Two-Phase Commit的縮寫,即二階段提交,是計算機網絡尤爲是在數據庫領域內,爲了使基於分佈式系統架構下的全部節點在進行事務處理過程當中可以保持原子性和一致性而設計的一種算法。一般,二階段提交協議也被認爲是一種強一致性協議,用來保證分佈式系統數據的一致性。網絡
二階段提交協議是將事務的提交過程拆分紅了兩個階段來進行處理,其執行流程以下:架構
一、事務詢問分佈式
協調者向全部的參與者發送事務內容,詢問是否能夠執行事務提交操做,並開始等待各參與者響應。學習
二、執行事務計算機網絡
各參與者節點執行事務操做,並將Undo和Redo信息記入事務日誌中。設計
三、各參與者向協調者反饋事務詢問的響應日誌
若是參與者成功執行了事務操做,那麼就反饋給協調者Yes響應,表示事務能夠執行;若是參與者沒有成功執行事務,那麼就反饋給協調者No響應,表示事務不能夠執行。事務
協調者會根據各參與者的反饋狀況來決定最終是否能夠進行事務提交操做,正常狀況下,包含如下兩種可能:
執行事務提交:假如協調者從全部的參與者得到的反饋都是Yes響應,那麼就會執行事務提交。
一、 發送提交請求
協調者向全部參與者節點發出Commit請求。
二、事務提交
參與者接收到Commit請求後,會正式執行事務提交操做,並在完成提交以後釋放在整個事務執行期間佔用的事務資源。
三、 反饋事務提交結果
參與者在完成事務提交以後,向協調者發送Ack消息。
四、完成事務
協調者接收到全部參與者反饋的Ack消息後,完成事務。
中斷事務:假如任何一個參與者向協調者反饋了No響應,或者在等待超時以後,協調者尚沒法接收到全部參與者的反饋響應,那麼就會中斷事務。
一、發送回滾請求
協調者向全部參與者節點發出Rollback請求。
二、事務回滾
參與者接收到Rollback請求後,會利用其在階段一中記錄的Undo信息來執行事務回滾操做,並在完成回滾以後釋放在整個事務執行期間佔用的資源。
三、反饋事務回滾結果
參與者在完成事務回滾以後,向協調者發送Ack消息。
四、中斷事務
協調者接收到全部參與者反饋的Ack消息後,完成事務中斷。
簡單地講,二階段提交將一個事務的處理過程分爲了投票和執行兩個階段,其核心是對每一個事務都採用先嚐試後提交的處理方式,所以也能夠將二階段提交看做一個強一致性的算法。
一、同步阻塞
在執行過程當中,全部參與該事務操做的邏輯都處理於阻塞狀態。即節點之間在等待對方的相應消息時,它將什麼也作不了。特別是,當一個節點在已經佔有了某項資源的狀況下,爲了等待其餘節點的響應消息而陷入阻塞狀態時,當第三個節點嘗試訪問該節點佔有的資源時,這個節點也將連帶陷入阻塞狀態。
二、 單點問題
一旦協調者出現問題,那麼整個二階段提交流程將沒法運轉,更爲嚴重的是,若是協調者是在階段二中出現問題的話,那麼其餘參與者將會一直處於鎖定事務資源的狀態中,而沒法繼續完成事務操做。
三、 數據不一致
當協調者向全部的參與者發送Commit請求以後發生了局部網絡異常或者是協調者在還沒有發送完Commit請求以前自身發生了崩貴,致使最終只有部分參與者收到了Commit請求,因而整個分佈式系統便出現了數據不一致性現象。
三、太過保守
二階段提交沒有設計較爲完善的容錯機制,任意一個節點的失敗都會致使整個事務的失敗。
3PC,是Three-Phase Commit的縮寫,即三階段提交,是2PC的改進版。由CanCommit、PreCommit和doCommit三個階段組成的事務處理協議。
一、事務詢問。
協調者向全部的參與者發送一個包含事務內容的canCommit請求,詢問是否能夠執行事務提交操做,並開始等待各參與者的響應。
二、各參與者向協調者反饋事務詢問的響應。
參與者在接收到來自協調者的canCommit請求後,正常狀況下,若是其自身認爲能夠順利執行事務,那麼會反饋Yes響應,並進入預備狀態,不然反饋No響應。
協調者會根據各參與者的反饋狀況來決定最終是否能夠進行事務提交操做,正常狀況下,包含如下兩種可能:
執行事務預提交;假如協調者從全部的參與者得到的反饋都是Yes響應,那麼就會執行事務的預執行。
一、發送預提交請求
協調者向全部參與者節點出preCommit的請求,並進入prepared階段。
二、事務預提交
參與者接收到preCommit請求後,會執行事務操做,並將Undo和Redo信息記錄到事務日誌中。
三、各參與者向協調者反饋事務執行的響應
若是參與者成功執行了事務操做,那麼就會反饋給協調者Ack響應,同時等待最終的指令:提交(commit)或停止(abort)。
中斷事務:假如任何一個參與者向協調者反饋了No響應,或者在等待超時以後,協調者尚沒法接收到全部者的反饋響應,那麼就會中斷事務。。
一、發送中斷請求
協調者向全部參與者節點發生abort請求。
二、中斷事務
不管是收到來自協調者的abort請求,或者是在等待協調者請求過程當中出現超時,參與者都會中斷事務。
該階段將進行真正的事務提交,會存在如下兩種可能的狀況。
執行提交
一、發送提交請求
進入這一階段,假如協調者處於正常狀態,而且它接收到了來自全部參與者的Ack響應,那麼它將從「預提交」狀態轉換到「提交」狀態,並向全部參與者發送doCommit請求。
二、事務提交
參與者接收到doCommit請求後,會正式執行事務提交操做,並在完成提交以後釋放在整個事務執行期間佔用的事務資源。
三、反饋事務提交結果
參與者在完成事務提交以後,向協調者發送Ack消息。
四、完成事務
協調者接收到全部參與者反饋的Ack消息後,完成事務。
中斷事務
進入這一階段,假設協調者處於正常工做狀態,而且有任意一個參與者向協調者反饋了No響應,或者在等待超時以後,協調者尚沒法接收到全部參與者的反饋響應。
一、發送中斷請求
協調者向全部的參與者節點發送abort請求。
二、事務回滾
參與者接收到abort請求後,會利用其在階段二中記錄的Undo信息來執行事務回滾操做,並在完成回滾以後釋放在整個事務執行期間佔用的資源。
三、反饋事務回滾結果
參與者在完成事務會滾以後,向協調者發送Ack消息。
四、中斷事務
協調者接收到全部參與者反饋的Ack消息後,中斷事務。
在doCommit階段,若是參與者沒法及時接收到來自協調者的doCommit或者abort請求時,在等待超時以後,會繼續進行事務的提交。即當進入第三階段時,因爲網絡超時等緣由,雖然參與者沒有收到協調者的doCommit或者abort請求,但事務仍然會提交。
三階段提交協議與兩階段提交協議有兩個主要的不一樣:
增長了一個詢問階段,詢問階段能夠確保儘量早的發現沒法執行操做而須要停止的行爲,可是它並不能發現全部的這種行爲,只會減小這種狀況的發生。
在準備階段之後,協調者和參與者執行的任務中都增長了超時,一旦超時,協調者和參與者都繼續提交事務,默認爲成功,這也是根據機率統計上超時後默認成功的正確性最大。
三階段提交協議在去除阻塞的同時也引入了新的問題,那就是在參與者接收到preCommit消息後,若是網絡出現分區,此時協調者所在的節點和參與者沒法進行正常的網絡通訊,在這種狀況下,該參與者依然會進行事務的提交,這必然出現數據的不一致性。
Paxos算法是一種基於消息傳遞且具備高度容錯特性的一致性算法,是目前公認的解決分佈式一致性問題最有效的算法之一。
在古希臘的有一個叫作Paxos的小島,島上採用會議的形式來經過法令,議會中的議員經過信使進行消息的傳遞。值得注意的是,議員和信使都是兼職的,他們隨時有可能會離開議會廳,而且信使可能會重複的傳遞信息,也可能一去不復返。所以,議會協議要保證在這個狀況下法令仍然可以正確的產生,而且不會出現衝突。
首先將議員的角色分爲Proposer,Acceptor,和Learner(容許身兼數職)。Proposer提出提案,提案信息包括提案編號和提議的value;Acceptor收到提案後能夠接受(accept)提案,若是提案得到多數Acceptors的接受,則稱該提案被批准(chosen);Learner只能「學習」被批准的提案。劃分角色後,就能夠更精確的定義問題:
決議(value)只有在被Proposers提出後才能被批准(未經批准的決議稱爲「提案」(proposal);
在一次Paxos算法的執行實例中,只批准(chosen)一個value;
learners只能得到被批准(chosen)的value。
由上面的三個語義可演化爲下面幾個約束:
P1:一個Acceptor必須接受(accept)第一次收到的提案。
P2:一旦一個具備value v的提案被批准(chosen),那麼以後批准(chosen)的提案必須具備value v。
P2a:一旦一個具備value v的提案被批准(chosen),那麼以後任何Acceptor再次接受(accept)的提案必須具備value v。
P2b:一旦一個具備value v的提案被批准(chosen),那麼之後任何Proposer提出的提案必須具備value v。
P2c:若是一個編號爲n的提案具備value v,那麼存在一個多數派,要麼他們中全部人都沒有接受(accept)編號小於n的任何提案,要麼他們已經接受(accept)的全部編號小於n的提案中編號最大的那個提案具備value v。
P1a:當且僅當Acceptor沒有迴應過編號大於n的prepare請求時,Acceptor接受(accept)編號爲n的提案。
階段一(決議提出)
一、Proposer選擇一個提案編號M,而後向Acceptors的某個超過半數的子集成員發送編號爲M的prepare請求。
二、若是一個Acceptor接收到一個編號爲M的pepare請求,且編號M大於該Acceptor已經響應的全部prepare請求的編號,那麼它就會將它已經批准過的最大編號的提案做爲響應反饋給Proposer,同時該Acceptor會承諾不會再批准任何編號小於M的提案。
階段二(批准階段)
一、若是Proposer收到來自半數以上的Acceptor對其發出的編號爲M的prepare請求的響應,那麼它就會發送一個針對[M,V]提案的accept請求給Acceptor。注意,V的值就是收到響應中編號最大的提案的值,若是響應中不包含任何提案,那麼它就是任意值。
二、若是Acceptor收到這個針對[M,V]提案的Accept請求,只要該Acceptor還沒有對編號大於M的prepare請求作出響應,它就能夠經過這個提案。
如何讓Learner獲取提案,大致能夠有如下幾種方案:
方案一
Learner獲取一個已經被選定的提案的前提是,該提案已經被半數以上的Acceptor批准了。所以,最簡單的作法就是一旦Acceptor批准了一個提案,就將該提案發送給全部的Learner。這種作法雖然可讓Learner儘快地獲取被選定的提案,可是卻須要讓每一個Acceptor與全部Learner逐個進行一次通訊,通訊的次數至少爲兩者個數的乘積。
方案二
咱們可讓全部的Acceptor將它們對提案的批准狀況,統一發送給一個特定的Learner(這樣的Learner稱爲「主Learner」),假定Learner之間能夠經過消息通訊來互相感知提案的選定狀況。當主Learner被通知一個提案被選定時,它會負責通知其餘的Learner。
方案二雖然須要多一個步驟才能將提案通知到全部的Learner,但其通訊次數卻大大減小了,一般只是Acceptor和Learner的個數總和。但同時,該方案引入了一個新的不穩定因素;主Learner隨時可能出現故障。
方案三
將主Learner的範圍擴大,即Acceptor能夠將批准的提案發送給一個特定的Learner集合,該集合中的每一個Learner均可以在一個提案被選定後通知全部其餘的Learner。這個Learner集合中的Learner個數越多,可靠性就越好,但同時網絡通訊的複雜度也就越高。
假設存在這樣一種極端狀況,有兩個Proposer依次提出了一系列編號遞增的議案,可是最終都沒法被選定,具體流程以下:
Proposer P1提出了一個編號爲M1的提案,並完成了上述階段一的流程。但與此同時,另一個Proposer P2提出了一個編號爲M2(M2>M1)的提案,一樣也完成了階段一的流程,因而Acceptor已經承諾再也不批准編號小於M2的提案了。所以,當P1進入階段二的時候,其發出的accept請求將被Acceptor忽略,因而P1再次進入階段一併提出了一個編號爲M3,(M3>M2)的提案,而這又致使P2在第二階段的accept請求被忽略,以此類推,提案的選定過程將陷入死循環。
爲了保證Paxos算法流程的可持續性,以免陷入上述提到的「死循環」,就必須選擇一個主Proposer,並規定只有主Proposer才能提出議案。這樣一來,只要主Proposer和過半的Acceptor可以正常進行網絡通訊,那麼但凡主Proposer提出一個編號更高的提案被提出或正在接受批准,那麼它會丟棄當前這個編號較小的提案,並最終可以選出一個編號足夠大的提案。所以,若是系統中有足夠多的組建(包括Proposer、Acceptor和其餘網絡通訊組建)可以正常工做,那麼經過選擇一個主Proposer,整套Paxos算法流程就可以保持活性。
從Paxos到Zookeeper分佈式一致性原理與實踐