Google Chubby的做者Mike Burrows說過這個世界上只有一種一致性算法,那就是Paxos,其它的算法都是殘次品。算法
Paxos算法問世已經有將近30年的歷史了,是目前公認最有效的解決分佈式場景下一致性問題的算法之一,可是缺點是比較難懂,工程化比較難。本文但願可以輔以圖例和通俗易懂的實例把Paxos算法講清楚。安全
在分佈式系統中,在異步通信的過程當中,總會發生網絡波動、機器宕機等狀況,那麼如何在這樣複雜的狀況下,快速且安全的就某一數值達成一致呢?Paxos算法主要就是解決此類問題,在布式鎖、主從複製、命名服務、分佈式協調等常見場景下,Paxos算法都有着普遍的應用。網絡
參與角色異步
在Paxos算法中,全部的參與者被分爲了如下幾個角色分佈式
角色 | 分工 | 參與決策 |
---|---|---|
Proposer | 提出提案,提案:[編號Id,提議的Value] | √ |
Acceptor | 接收提案,批准/拒絕提案,當提案被大多數的Acceptor(Quorum)批准後即爲被選定的提案(Chosen) | √ |
Learner | 學習(Learn)最新被選定的提案 | × |
Paxos算法正確的必要條件學習
如今將算法的參與者分爲了這樣三個角色,那麼是爲了讓他們完成什麼樣的工做目標呢?3d
一個分佈式算法有兩個最重要的屬性:活性、安全性code
咱們能夠從這兩個方面來考察Paxos算法的正確性cdn
活性:blog
保證最終有一個提案會被選定,當提案被選定後,進程最終最終也能獲取到被選定的提案。
安全性:
那麼咱們下面來看看具體的算法流程
算法描述來自於倪超《從Paxos到ZooKeeper分佈式一致性原理與實踐》
階段一
階段二
若是Proposer收到半數以上Acceptor對其發出的編號爲N的Prepare請求的響應,那麼它就會發送一個針對[N,V]提案的Accept請求給半數以上的Acceptor。注意:V就是收到的響應中編號最大的提案的value,若是響應中不包含任何提案,那麼V就由Proposer本身決定。
若是Acceptor收到一個針對編號爲N的提案的Accept請求,只要該Acceptor沒有對編號大於N的Prepare請求作出過響應,它就接受該提案。
乾巴巴的算法描述可能比較難以理解,因此從圖解分佈式一致性協議Paxos這裏借來一個很簡明的圖來輔助理解。
從上圖看到,做爲Acceptor只須要存儲批准/保證過的提案的最大編號(MaxN),批准過的提案的最大編號(AcceptN),批准過的提案的Value值(AcceptV),而後經過階段一(2)和階段二(2)的兩種規則進行對提案的審批,即可以保證審批的安全性。
而Proposer須要保證在階段一(1)時提出的提案編號惟一且單調遞增,而在階段二(1)時只對獲取到了足夠多的保證(即得到了大多數Acceptor對Proposer的保證)的提案進行提交,即可以保證提案申請的安全性。
那麼爲何這樣可以知足上面所述的分佈式算法的安全性呢?這個要從Paxos算法的推導來看。完整的推導過程能夠在wikipedia上看到。
下面我來談一談個人理解,在推導過程當中有這麼幾個重要的約束:
P1:一個 acceptor 必須接受(accept)第一次收到的提案。
P1a:當且僅當acceptor沒有迴應過編號大於n的prepare請求時,acceptor接受(accept)編號爲n的提案。
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。
他們之間的關係能夠用下圖來講明
當Acceptor僅可批准一個提案時,僅依靠P1,也是可以只批准出一個Value的,可是在這種狀況下,頗有可能須要屢次重複投票過程纔可以達到一致性的狀態,也就是說雖然可以保證安全性,可是犧牲了部分的活性。以下圖所示:
Proposer老是可以優先得到同機房內的Acceptor的批准,可是很難得到其餘機房的Acceptor的批准,這時ProposerA、ProposerB、ProposerC各得到一票,每一個Proposer的提案都沒有經過,此時Proposer只能生成編號更大的提案,以期許可以得到大多數的Acceptor(2個)的批准,也許將來不久,某個lucky dog最終可以得到大多數的Acceptor的批准,可是咱們已經等的花兒都謝了。
因此爲了可以快速到達一致性,又引入了P2c和P1a,在P1a中解除了Acceptor只能批准一個提案的限制,可是增長了對於批准提案的編號的限制,在P2中增長了對Proposer提出提案的Value值的限制,這兩個限制帶來的直接效果有兩個:
這樣講可能仍是比較難以理解,咱們如今就上面那個例子作一個圖示,分別看看選出提案爲A、和提案爲B的流程。
如圖四所示,最終造成了值爲A的提案。
如圖五所示,最終造成了值爲B的提案。
這時候停下來思考一下,嚴格來講,上面描述的犧牲活性問題並無解決,只是下降了發生了的機率,在極端狀況下仍是可以發生一種相似於「活鎖」的狀態的。以下圖所示
在極端狀況下,這種循環會一直進行下去。因此爲了解決這種問題,又提出了Multi-Paxos算法,Multi-Paxos具體算法在這裏不作陳述,它是在Proposer中又搞了一個Leader的概念,在初期,全部的Proposer中競選出一個Leader,而後只有Leader可以向Acceptor提出提案,當Leader發生問題時,再競選一個Leader出來,沒有了Proposer的競爭,兩階段也變成了一階段,提升了效率,也解決了活鎖的問題。可是仔細想一想,競選Leader的過程當中也可能會發生活鎖的,我估計這也是Raft算法被提出來的真正緣由(狗頭),畢竟最後繞了一大圈,最終仍是搞出了單點的Leader出來進行管理,還不如用上面P1+重試的機制選出Leader,效率平時是差很少的,僅在選舉Leader時會比較慢而已。
本文分析了Paxos算法的應用價值和具體實現原理,但願能讓你們在學習Paxos算法的過程當中可以少掉一點頭髮。後續可能還會更新我對Raft算法的理解。
歡迎關注個人博客:王老魔的代碼備忘錄