2PC/3PC、paxos與ZAB協議

2PC

即Two-phase Commit,參與者將操做成敗通知協調者,再由協調者根據全部參與者的反饋情報決定各參與者是否要提交操做仍是停止操做。php

 

                  

 

第一階段(提交請求階段)算法

  1. 協調者節點向全部參與者節點詢問是否能夠執行提交操做,並開始等待各參與者節點的響應。
  2. 參與者將鎖定資源,節點執行詢問發起爲止的全部事務操做,並將Undo信息Redo信息寫入日誌。
  3. 各參與者節點響應協調者節點發起的詢問。若是參與者節點的事務操做實際執行成功,則它返回一個"贊成"消息;若是參與者節點的事務操做實際執行失敗,則它返回一個"停止"消息。

第二階段(提交執行階段)數據庫

成功安全

當協調者節點從全部參與者節點得到的相應消息都爲"贊成"時:服務器

  1. 協調者節點向全部參與者節點發出"正式提交"的請求。
  2. 參與者節點正式完成操做,並釋放在整個事務期間內佔用的資源。
  3. 參與者節點向協調者節點發送"完成"消息。
  4. 協調者節點收到全部參與者節點反饋的"完成"消息後,完成事務。

失敗網絡

若是任一參與者節點在第一階段返回的響應消息爲"終止",或者 協調者節點在第一階段的詢問超時以前沒法獲取全部參與者節點的響應消息時:併發

  1. 協調者節點向全部參與者節點發出"回滾操做"的請求。
  2. 參與者節點利用以前寫入的Undo信息執行回滾,並釋放在整個事務期間內佔用的資源。
  3. 參與者節點向協調者節點發送"回滾完成"消息。
  4. 協調者節點收到全部參與者節點反饋的"回滾完成"消息後,取消事務。

存在的問題app

  1. 阻塞問題。當有部分參與者宕機或者與協調者網絡通訊故障,那麼正常回應的參與者將進入阻塞狀態,沒法對鎖定的資源進行任何操做,只有等待超時中斷事務,極大的限制了系統的性能。
  2. 單點問題。2PC徹底依賴協調者的正常工做,一旦協調者出現問題,那麼整個協議將癱瘓,若是協調者在階段二中出現問題的話,那麼其餘參與者將會一直處於鎖定事務資源的狀態中。
  3. 參與者直到接受到doCommit請求才知道協調者決定。假設在階段2協調者與第一個參與者A在通訊後雙雙宕機,那麼剩下的機器即便從新選擇協調者後,新的協調者不管是容許仍是拒絕執行,那麼都有可能與A機器的數據不一致。

3PC

      3PC是2PC的改進版本,將2PC的第一階段:提交事務階段一分爲二,造成CanCommit、PreCommit和doCommit三個階段組成的事務處理協議。分佈式

                                

                 

      3PC爲了解決問題3參與者直到接受到doCommit請求才知道協調者決定而加入了canCommit階段。這樣即便出現了3問題,剩下的參與者也知道這次事務要提交,由於通過前兩階段的詢問,不管是參與者仍是協調者都已知道這次事物是要提交的。性能

     爲了解決單點問題和阻塞問題,3PC在參與者這邊也引入了超時機制。在階段一和階段二不管協調者仍是參與者故障,那麼在等待超時後,將拒絕這次事務,故障機器在恢復後,也會拒絕這次事務。在階段三一旦參與者沒法及時收到來自協調者的信息以後,他會默認執行commit,而不會一直持有事務資源並處於阻塞狀態,而等到故障機器恢復,也會對事務進行提交從而達到數據的一致性。

     不少博客說3PC爲了解決阻塞問題加入了canCommit階段,說canCommit階段沒有鎖定資源,下降了持有資源的時間。我認爲canCommit階段仍是持有了資源的,由於階段二隻是告知各參與者協調者所作的決定,並且階段三的超時自動提交依賴的是一階段參與者對事務的保證。

存在的問題

  1. 雖然3PC解決了大多數場景的一致性問題,可是少數場景仍是會出現數據不一致的狀況。好比在發送preCommit中協調者故障,那麼收到參與者在收到請求後將進入階段三,而剩下的參與者還在階段二,根據超時機制將產生數據的不一致。
  2. 3PC在要進行3輪通訊,可想而知一輪事務的決策將是漫長的,對系統的負載也要求極高。

paxos

      瞭解了2PC和3PC以後,咱們能夠發現,不管是二階段提交仍是三階段提交都沒法完全解決分佈式的一致性問題。Google Chubby的做者Mike Burrows說過, there is only one consensus protocol, and that’s Paxos」 – all other approaches are just broken versions of Paxos. 意即世上只有一種一致性算法,那就是Paxos。

     首先將一次決議角色分爲proposers,acceptors,和learners(容許身兼數職)。proposers提出提案,提案信息包括提案編號和提議的value;acceptor收到提案後能夠接受(accept)提案,若提案得到多數acceptors的接受,則稱該提案被批准(chosen);learners只能「學習」被批准的提案。

      而後一致性要知足安全性和活性 :

  • 安全性:只有被提出的提案才能被選定;只能有一個值被選定;若是某個proposer認爲提案被選定 ,那麼這個提案必須是真的被選定
  • 活性:一次決議總有提案被選定 

      那如今咱們有P1,P2,P3三個進程,要經過一個關於V的決議,顯而易見咱們獲得一個約束:

      P1 acceptor必須接受它第一個收到的提案 

     那麼咱們就有這樣一個場景,P1決定令V=1,P2決定令V=2,P3決定令V=3,這種場景下acceptors沒法造成多數派,因此咱們須要新的約束。由於進程都是平等的,因此不能對進程進行約束,好比說拒絕某個進程的提案,不讓某個進程提出提案,因此咱們轉而提案進行加強。考慮維護一個全局的遞增序列對提案進行編號(P_ID),這樣P1,P2,P3就有足夠的憑證進行裁決,不妨假設接受大的P_ID。假設P3的P_ID最大,那麼必定時間後,三個進程中V的值將等於3,可是在這必定時間中的某段時間,V的值將被決定爲2(P2的提案提早於P3的提案到達P1),這違反了一致性的安全性。那麼怎麼讓P1拒絕P2或者P3的提案呢,P1在接受到P2的提案以前並無接受到任何消息,P2的提案到達時也只知道本身接受了P2的提案。對acceptor已經沒有更強的約束了,因此咱們轉而對proposer進行約束。既然只能選定一個值,那麼咱們要求proposer提出提案選值的時候必須是存活acceptors中的批准的P_ID最大的提案值。

      P2 proposer提出提案選值的時候必須是回覆acceptors中的接受的P_ID最大的提案值

     固然咱們來看看徹底體的算法是怎麼描述的

階段一

  1. Proposer選擇一個提案編號M,而後向超過半數的Acceptor發送編號爲M的prepare請求。
  2. 若是一個Acceptor收到一個編號爲M的prepare請求,且編號M大於該Acceptor已經響應過的全部prepare請求的編號,那麼他會將他已經批准過的最大編號的提案做爲響應反饋給Proposer,同時承諾不會再批准任何編號小於M的提案。

階段二

  1. 若是Proposer收到半數以上的Acceptor對於其發出的編號爲M的prepare請求的響應,那麼他會發送一個編號爲M值爲V的Accept請求給Acceptor。V的值就是收到的響應中最大的提案的值,若是響應不包含任何提案,那麼V能夠是任意值。
  2. 若是Acceptor收到編號爲M的提案的Accept請求,只要改Acceptor還沒有對編號大於M的prepare請求作出響應,他就能夠經過這個提案。 

     那麼如同2PC/3PC 中的協調者和部分參與者doCommit而致使的數據一致性問題解決了嗎?

     咱們假設有五個進程ABCDE,某時刻A提出提案P1令V=1,並對BC發送了prepare請求,BC收到prepare請求回覆給了A,A對B發送的V=1的accept請求後終止,B在接受到Accept請求後也終止了。此時E深感本身責任重大,提出V=2的提案P2併發送prepare請求給CD,當C在接受到P2的preapre後,將回復P1{V=1}給E,E在收到C的響應後根據協議只能含淚把值改成1在發送Accept請求。

     這樣看來好像是隻要先有提案提出,那麼之後提案的值都是第一次提案的值,這樣好像也知足了安全性和活性。可是根據Paxos算法,也沒有明確要求提案的值不能變。那麼被決定以前提案的值能不能變呢?有這樣一個場景,仍是有五個進程ABCDE,某時刻A提出提案P1令V=1,並對BC發送了prepare請求,可是可憐的C在還未收到prepare請求時掛掉了,固然A也沒收到多數派的響應,天然不會發送accept請求。然後C又成功上線,E命運使然的提出V=2的提案P2併發送prepare請求給CD,而後獲得了CD的接受造成了本身的多數派使得P2被批准,可是若是E發送prepare請求給含有A或B的多數派那結果將徹底不一樣。而不管哪一種狀況度沒有違反活性和安全性的要求,paxos的做用也僅僅是肯定一個值且在分佈式系統保持一致性,至於你想說的是個人值能夠進行屢次改寫啊,那就要將paxos和狀態機聯繫起來。

ZAB

      paxos經過多數派和限制提案的值完美的解決了咱們的問題,可是理論到實際是個艱難的過程。好比怎樣在分佈式環境下維持一個全局惟一遞增的序列,若是是靠數據庫的自增主鍵,那麼整個系統的穩定和性能的瓶頸全都集中於這個單點。paxos算法也沒有限制Proposer的個數,Proposer個數越多,那麼達成一致所形成的碰撞將越多,甚至產生活鎖,若是限制Proposer的個數爲一個,那麼就要考慮惟一的Proposer崩潰要怎麼處理。paxos只是肯定了一個值,離咱們實際運用想要屢次讀寫還有距離,上文也說過想要屢次讀寫,要將paxos和狀態機聯繫起來。

     ZAB協議分爲2個部分,讓咱們來看看ZAB協議是怎麼解決這些問題的:

     (如下內容來源於http://www.jianshu.com/p/fb527a64deee

  • 廣播(boardcast):Zab 協議中,全部的寫請求都由 leader 來處理。正常工做狀態下,leader 接收請求並經過廣播協議來處理。
  • 恢復(recovery):當服務初次啓動,或者 leader 節點掛了,系統就會進入恢復模式,直到選出了有合法數量 follower 的新 leader,而後新 leader 負責將整個系統同步到最新狀態

廣播(boardcast)

廣播的過程其實是一個簡化的二階段提交過程:

  1. Leader 接收到消息請求後,將消息賦予一個全局惟一的 64 位自增 id,叫作:zxid,經過 zxid 的大小比較便可實現因果有序這一特性。
  2. Leader 經過先進先出隊列(經過 TCP 協議來實現,以此實現了全局有序這一特性)將帶有 zxid 的消息做爲一個提案(proposal)分發給全部 follower。
  3. 當 follower 接收到 proposal,先將 proposal 寫到硬盤,寫硬盤成功後再向 leader 回一個 ACK。
  4. 當 leader 接收到合法數量的 ACKs 後,leader 就向全部 follower 發送 COMMIT 命令,同事會在本地執行該消息。
  5. 當 follower 收到消息的 COMMIT 命令時,就會執行該消息 

      相比於完整的二階段提交,Zab 協議最大的區別就是不能終止事務,follower 要麼回 ACK 給 leader,要麼拋棄 leader,在某一時刻,leader 的狀態與 follower 的狀態極可能不一致,所以它不能處理 leader 掛掉的狀況,因此 Zab 協議引入了恢復模式來處理這一問題。從另外一角度看,正由於 Zab 的廣播過程不須要終止事務,也就是說不須要全部 follower 都返回 ACK 才能進行 COMMIT,而是隻須要合法數量(2f+1 臺服務器中的 f+1 臺) 的follower,也提高了總體的性能。

恢復(recovery)

     因爲以前講的 Zab 協議的廣播部分不能處理 leader 掛掉的狀況,Zab 協議引入了恢復模式來處理這一問題。爲了使 leader 掛了後系統能正常工做,須要解決如下兩個問題:

  • 已經被處理的消息不能丟
  • 被丟棄的消息不能再次出現

已經被處理的消息不能丟

      這一狀況會出如今如下場景:當 leader 收到合法數量 follower 的 ACKs 後,就向各個 follower 廣播 COMMIT 命令,同時也會在本地執行 COMMIT 並向鏈接的客戶端返回「成功」。可是若是在各個 follower 在收到 COMMIT 命令前 leader 就掛了,致使剩下的服務器並無執行都這條消息。

      爲了實現已經被處理的消息不能丟這個目的,Zab 的恢復模式使用瞭如下的策略:

  1. 選舉擁有 proposal 最大值(即 zxid 最大) 的節點做爲新的 leader:因爲全部提案被 COMMIT 以前必須有合法數量的 follower ACK,即必須有合法數量的服務器的事務日誌上有該提案的 proposal,所以,只要有合法數量的節點正常工做,就必然有一個節點保存了全部被 COMMIT 消息的 proposal 狀態。
  2. 新的 leader 將本身事務日誌中 proposal 但未 COMMIT 的消息處理。
  3. 新的 leader 與 follower 創建先進先出的隊列, 先將自身有而 follower 沒有的 proposal 發送給 follower,再將這些 proposal 的 COMMIT 命令發送給 follower,以保證全部的 follower 都保存了全部的 proposal、全部的 follower 都處理了全部的消息。
    經過以上策略,能保證已經被處理的消息不會丟

被丟棄的消息不能再次出現

       這一狀況會出如今如下場景:當 leader 接收到消息請求生成 proposal 後就掛了,其餘 follower 並無收到此 proposal,所以通過恢復模式從新選了 leader 後,這條消息是被跳過的。 此時,以前掛了的 leader 從新啓動並註冊成了 follower,他保留了被跳過消息的 proposal 狀態,與整個系統的狀態是不一致的,須要將其刪除。        Zab 經過巧妙的設計 zxid 來實現這一目的。一個 zxid 是64位,高 32 是紀元(epoch)編號,每通過一次 leader 選舉產生一個新的 leader,新 leader 會將 epoch 號 +1。低 32 位是消息計數器,每接收到一條消息這個值 +1,新 leader 選舉後這個值重置爲 0。這樣設計的好處是舊的 leader 掛了後重啓,它不會被選舉爲 leader,由於此時它的 zxid 確定小於當前的新 leader。當舊的 leader 做爲 follower 接入新的 leader 後,新的 leader 會讓它將全部的擁有舊的 epoch 號的未被 COMMIT 的 proposal 清除。     

相關文章
相關標籤/搜索