分散式應用中的一致性協議

witchiman發表於2018-11-22

在一個分散式系統中,需要一個規定來保證資料的一致性、各節點服務的容錯性等等,這個規定就是一致性協議。常見的分散式協議有2PC、3PC、Paxos和raft等。

2PC

即Two-Phase Commit,二階段提交。目前大多數的關係型資料庫都是採用2PC協議來完成分散式事務處理的。

執行過程

2PC中包含兩個角色協調者和參與者,其分為兩個階段:

階段一,提交事務請求,也稱投票階段。

  • 事務詢問。協調者會向所有的參與者傳送事務,詢問是否可以執行事務提交操作,並開始等待各參與者的響應。
  • 執行事務。各參與者執行事務操作,並將undo和redo資訊記入事務日誌中。
  • 各參與者向協調者反饋事務詢問的響應。如果參與者成功執行一事務操作, 反饋給協調者yes響應,表示事務可以執行;如果沒有成功執行事務,就反饋no給協調者,表示事務不可執行。

階段二,執行事務提交。

​ 在階段二,協調者會根據各參與者的反饋情況決定最終是否可以進行事務提交操作。

  • 傳送提交請求。協調者會一一向參與者傳送commit請求。
  • 事務提交請求。當參與者收到commit請求後,會執行事務提交操作,並在完成提交之後釋放在整個事務執行期間佔用的事務資源。
  • 反饋事務提交結果。參與者在完成事務提交之後,向協調者傳送ack訊息。
  • 完成事務。協調者收到所有參與者反饋的ack訊息後,完成事務。

中斷事務,當參與者向協調者反饋了no響應,或者協調者在等待參與者反饋訊息的過程中超時了,就會觸發中斷事務。

  • 傳送回滾請求。協調者向所有參與者發出rollback請求。
  • 事務回滾。參與者收到rollback請求後,會複用在階段一中記錄的undo資訊來執行回滾操作,並在完成回滾之後釋放在整個事務執行期間佔用的資源。
  • 反饋事務回滾結果。參與者完成事務回滾之後,向協調者傳送ack訊息。
  • 中斷完成。協調者接收到所有參與者反饋的ack訊息後,完成事務中斷。

優缺點

優點:原理簡單,實現方便。

缺點:同步阻塞、單點問題、資料不一致和沒有完善的容錯機制。

  • 同步阻塞。在二階段提交的執行過程中,所有參與該事務操作的邏輯都處於阻塞狀態,即各個參與者在等待其他參與者響應的過程中,無法再進行其它操作。
  • 單點問題。一旦協調者出現問題,整個二階段的提交流程就無法正常地進行;並且其它參與者會一直處於鎖定事務資源的狀態中,無法繼續完成事務操作。
  • 資料不一致。在階段二中,當協調者向所有參與者傳送commit請求後,如果發生了區域性的網路異常或者是協調者在尚未傳送完commit請求之前自己崩潰了,導致最終只有部分參與者收到了commit。最終,只有收到commit請求的參與者才會進行事務的提交,整個分散式系統就會出現資料不一致的情況。
  • 沒有完善的容錯機制。協調者在詢問參與者事務提交的過程中,如果參與者由於種種原因沒有返回訊息,導致協調者不能獲取 所有參與者的響應訊息,協調者只能通過超時的機制來判斷是否需要中斷事務。2PC沒有設計較為完善的容錯機制 ,任意一個節點的失敗都會導致整個事務的失敗。

3PC

3PC即 Three-Phase Commit,是2PC的改進版,同樣有兩個角色協調者和參與者,不同的是將2PC的階段一分成兩步,所以它包含了三個階段CanCommit、PreCommit和DoCommit。

CanCommit

  • 事務詢問。協調者向所有的參與者傳送CanCommit請求,詢問是否可以執行事務操作,然後等待參與者的響應。
  • 參與者響應。參與者在收到協調者的CanCommit請求後,如果沒有什麼問題,就反饋yes,否則反饋no。

PreCommit

如果CanCommit正常進行,那麼進入執行事務提交。

  • 傳送提交請求。協調者向所有參與者傳送PreCommit請求,進入了Prepared階段。
  • 事務預提交。參與者接到PreCommit 請求後,會執行事務操作,並將undo和redo資訊記錄到事務日誌中去。
  • 參與者把事務執行的結果反饋給協調者。如果各參與者成功執行了事務操作,就反回ack響應,然後等待最終後Commit或abort的請求

如果第一步中有參與者返回了no,或者發生了超時,協調者就會中斷事務

  • 傳送中斷請求。協調者向所有參與者傳送abort請求。
  • 中斷事務。對參與者而言,不管是收到abort請求還是在等待協調者的過程中發生了超時,其都會中斷事務。

DoCommit

這一階段才開始真正的事務提交。

執行提交。

  • 傳送提交請求。如果協調者收到了所有參與者ack 響應,就會向所有的參與者再次傳送DoCommit請求。
  • 事務提交。當參與者收到協調者的DoCommit請求後,開始正式執行事務的提交操作,並在完成提交之後釋放在整個事務執行期間佔用的事務資源。
  • 反饋事務提交結果。參與者在完成事務提交之後 ,向協調者傳送ack訊息。
  • 完成事務。協調者接收到所有參與者反饋的ack訊息後,完成事務。

假如協調者在等待參與者的過程中發生了超時,或者有參與者返回了no的響應,那麼協調者會中斷事務。

  • 傳送中斷請求。協調者向所有參與者傳送abort請求。
  • 事務回滾。參與者在收到abort請求後,會利用上一步在日誌中記錄的undo 資訊來進行事務回滾操作,並釋放在執行整個事務過程中佔用的資源。
  • 反饋事務回滾結果。 參與者在完成事務的回滾之後,向協調者傳送ack響應。
  • 中斷事務。協調者收到所有的ack訊息後,中斷事務。

優缺點

3PC 相比2PC降低了參與者阻塞的範圍,並能能夠在出現故障後繼續達到一致。

不過也引入了新的問題,在階段二,如果發生區域性網路的故障,協調者與個別參與者無法進行正常的通訊,仍然會造成資料不一致的現象。

Paxos

2PC和3PC 都沒有這樣或那樣的問題,Paxos的出現很好地解決了這一問題,是目前公認的解決分散式一致性問題最有效的演算法之一。

Paxos中有3個參與者Proposer、Acceptor和Leaner,Proposer是發起提案的人,Acceptor是接受提案的人,Learner是信使在Proposer和Acceptor之間傳送議案。在Paxos中有一些條件限制:

  • 一個Acceptor必須批准他收到的第一個提案。
  • 每個提案會有一個編號M,這個編號是全域性遞增的;同樣每個提案本身也有一個值 Value。如果編號為M1的Value為V1的提案即[M1,V1]被選定了,那麼所有比編號M1更高的,且被選定的提案,其值必須為V1。
    • 如果[M1,V1]被選定了,那麼所以比M1更高的,那麼所有比編號M1更高的,且被Acceptor選定的提案,其值必須為V1。
    • 如果[M1,V1]被選定了,往下產生的編號更高的提案,其Value都為V1。
    • 對如果[M1,V1], 如果其被提出,肯定存在一個由半數以上的Acceptor組成的集合S,滿足以下條件:S中不存在任何批准過編號大於M1的Acceptor;S中批准的所以編號小於M1的提案中,編號最大的那個的Value值也是V1。

執行過程

Paxos的執行相當於一次投票的選舉過程,分為兩個階段。

階段一

  • Proper選擇一個提案的編號M1,然後向Acceptor傳送提案。
  • 如果一個Acceptor收到提案M1,並且這個提案的編號大於自己之前處理過的所有的提案,那麼它就會把它已經批准過的編號最大的提案返回給Proposer,同時該Proposer承諾不會再批准任何編號小於M1的提案。

階段二

  • 如果Proposer的提案受到半數以上的Acceptor的批准,那麼他就會傳送提案[M1, V1]的accept請求。V1是Proposer收到的響應中編號最大的提案的值,如果沒有,則V1可以為任意值 。
  • 當Acceptor收到提案時,如果他沒有批准過大於其編號M1的提案,那麼就可以批准M1。

當提案被半數Acceptror批准後,就會把這個提案傳送給Learner,由Learner最後將提案傳遞下去,這樣一個提案才算完成。

這篇文章 www.cnblogs.com/endsock/p/3… 中通過一個例子形象地描述了Paxos演算法,在一定程度上有助於理解Paxos演算法的原理。

但是這裡也沒有提到一個問題,就是當一個Acceptor接收到一個Proposer的提案時並響應時,有另外一個Proposer提了一個編號更高的提案,然後在Accetpor響應後又有另外一個Proposer提出編號更高的提案…這樣會造成一個死迴圈。解決這個問題的方法就是建立一個主Proposer,在一個Proposer集合中只有一個Proposer能發出提案的請求給Acceptor。

優缺點

Paxos很好地解決分散式系統中的阻塞和單點故障的問題,但是Paxos的複雜性增加了它在工程實踐中應用的難度。

raft

Raft是史丹佛的Diego Ongaro、John Ousterhout兩個人以易懂(Understandability)為目標設計的一致性演算法,在2013年釋出了論文:《In Search of an Understandable Consensus Algorithm》從2013年釋出到現在不過只有兩年,到現在已經有了十多種語言的Raft演算法實現框架,較為出名的有etcd,Google的Kubernetes也是用了etcd作為他的服務發現框架;由此可見易懂性是多麼的重要。

在瞭解Raft之前,我們先了解Consensus一致性這個概念,它是指多個伺服器在狀態達成一致,但是在一個分散式系統中,因為各種意外可能,有的伺服器可能會崩潰或變得不可靠,它就不能和其他伺服器達成一致狀態。這樣就需要一種Consensus協議,一致性協議是為了確保容錯性,也就是即使系統中有一兩個伺服器當機,也不會影響其處理過程。

為了以容錯方式達成一致,我們不可能要求所有伺服器100%都達成一致狀態,只要超過半數的大多數伺服器達成一致就可以了,假設有N臺伺服器,N/2 +1 就超過半數,代表大多數了。

Paxos和Raft都是為了實現Consensus一致性這個目標,這個過程如同選舉一樣,參選者需要說服大多數選民(伺服器)投票給他,一旦選定後就跟隨其操作。Paxos和Raft的區別在於選舉的具體過程不同。

在Raft中,任何時候一個伺服器可以扮演下面角色之一:

  • Leader: 處理所有客戶端互動,日誌複製等,一般一次只有一個Leader.
  • Follower: 類似選民,完全被動
  • Candidate候選人: 類似Proposer律師,可以被選為一個新的領導人。

執行過程

Raft階段分為兩個,首先是選舉過程,然後在選舉出來的領導人帶領進行正常操作,比如日誌複製等。下面用圖示展示這個過程:

  • 任何一個伺服器都可以成為一個候選者Candidate,它向其他伺服器Follower發出要求選舉自己的請求:
  • 其他伺服器同意了,發出OK。注意如果在這個過程中,有一個Follower當機,沒有收到請求選舉的要求,因此候選者可以自己選自己,只要達到N/2 + 1 的大多數票,候選人還是可以成為Leader的。
  • 這樣這個候選者就成為了Leader領導人,它可以向選民也就是Follower們發出指令,比如進行日誌複製。
  • 以後通過心跳進行日誌複製的通知。
  • 如果一旦這個Leader當機崩潰了,那麼Follower中有一個成為候選者,發出邀票選舉。
  • Follower同意後,其成為Leader,繼續承擔日誌複製等指導工作:

Splite Vote是因為如果同時有兩個候選人向大家邀票,這時通過類似加時賽來解決,兩個候選者在一段timeout比如300ms互相不服氣的等待以後,因為雙方得到的票數是一樣的,一半對一半,那麼在300ms以後,再由這兩個候選者發出邀票,這時同時的概率大大降低,那麼首先發出邀票的的候選者得到了大多數同意,成為領導者Leader,而另外一個候選者後來發出邀票時,那些Follower選民已經投票給第一個候選者,不能再投票給它,它就成為落選者了,最後這個落選者也成為普通Follower一員了。

動畫演示: thesecretlivesofdata.com/raft/

優缺點

熟悉或瞭解分佈性系統的開發者都知道一致性演算法的重要性,Paxos一致性演算法從90年提出到現在已經有二十幾年了,而Paxos流程太過於繁雜實現起來也比較複雜,可能也是以為過於複雜 現在我聽說過比較出名使用到Paxos的也就只是Chubby、libpaxos,搜了下發現Keyspace、BerkeleyDB資料庫中也使用了該演算法作為資料的一致性同步,雖然現在很廣泛使用的Zookeeper也是基於Paxos演算法來實現,但是Zookeeper使用的ZAB(Zookeeper Atomic Broadcast)協議對Paxos進行了很多的改進與優化,演算法複雜我想會是制約他發展的一個重要原因;說了這麼多隻是為了要引出本篇文章的主角Raft一致性演算法,沒錯Raft就是在這個背景下誕生的,文章開頭也說到了Paxos最大的問題就是複雜,Raft一致性演算法就是比Paxos簡單又能實現Paxos所解決的問題的一致性演算法。

參考資料:


相關文章