本文的目的是一步步講解paxos如何是如何推斷和完備的。首先要了解,分佈式一致性(consistency)和分佈式共識(consensus)並非一個東西來的,然而網上大部分的人都直接把分佈式共識翻譯爲分佈式一致性,致使像paxos,raft這樣的算法被誤認一致性算法,並拿來跟2pc,3pc作比較,這是不對的。以前我也一直存在疑惑,有次吃飯的時候跟同導師的一個小哥聊天的時候,他一語道破,雖然不屬於官方的解釋,可是理解起來卻格外的清晰:算法
一致性是要求全部的節點一致,共識算法是隻要大部分節點認可安全
仍是得說明,這個並非官方解釋,在筆者找到更合適的說法前,暫時先用這個代替。分佈式共識算法要解決的問題是什麼,在分佈式的狀況下,咱們常常會把數據分佈到不一樣的節點上,如何來保證這些數據儘量達成一致就是paxos要解決的問題了。在論文的一開始,做者就講明瞭分佈式共識算法要達成的目標。(ps:若是存在理解不正確的地方,歡迎指出,謝謝)less
爲了簡單的來說解整個過程,我決定把文中說起的三種以爲擬人化。首先負責提議的稱爲<font color="#0f88eb">議員(proposal)</font>,負責決策的是<font color="#0f88eb" >選民(acceptor)</font>,負責學習的是<font color="#0f88eb" >羣衆(learner)</font>。有幾個約束,各個角色之間互相通訊是利用異步消息隊列,可能存在不可達甚至宕機重啓的現象,可是沒有拜占庭錯誤(也就是有偷懶睡覺的,可是沒有叛徒,不會修改消息內容)。且咱們規定提案只有通過大多數人贊成才能肯定下來(也就是超過半數的人)。首先來看第一條約束異步
爲何有這麼一條呢?爲了達成一致,直觀的想法都是,若是接收到了一個消息我就選擇accept這個消息,由於我沒有合適的拒絕策略。就相似於,每次議員提出一個提案,底下的全部選民就都贊成了,除了那些打瞌睡的不知道這件事的除外。之因此有這一條約束,我以爲最主要的緣由是目前咱們沒法給出一個合適的拒絕策略。可是這個選擇策略很明顯也是有問題的,若是此時有多個議員同時提出提案,而後底下的選民們分別接受到了不一樣的提案並回復了,可是因爲選民分散致使沒有一個提案能被大多數的表明選中,這樣就致使了選舉失敗(存在活鎖現象)。所謂活鎖就是一直在提議卻一直沒法選擇出最終結果。那麼若是要知足P1的約束,且要求必定要有大多數的選民都贊成一個提案,則催生了另外一個條件,選民能夠選擇多個提案。好比A,B兩個議員同時提出了兩個提案,做爲選民k的你,能夠在贊成A的提案以後,又贊成B的提案。不過這樣你確定又會問,新的選擇策略就是全選嗎?確定不行的,因而做者提出,在提案的基礎上,咱們加入一個全局的編號,這個編號全局有序,誰編號大就選誰,這樣最公平(至於提案如何生成這個編號,不在論文的討論範圍,可是我還要囉嗦句,具體實現中,這個編號的生成是很巧妙,不是隨意遞增就能夠的,並且這個編號是後文判斷的基礎,很是重要)。瞎逼逼了這麼多,就是爲了說明一點,咱們沒有合適的拒絕策略/選擇策略因此選擇第一個達到的提案,可是爲了保證必定會有值能選出,則須要能夠選民具備選擇多個提案的能力(不少狀況下並無一個絕對有效的拒絕策略,那麼換種思路就是多樣化的選擇策略,可是最終只有一個能成功)。
那麼咱們如何保證一致性呢?來看看另外一個新的約束分佈式
P2實際上是個很強的約束,若是咱們能保證在一次paxos選舉中,若是已經有一個能被大多數acceptor接受的結果,那麼咱們就應該堅決不移的堅持這個結果,天然能得到最終一致性。從前文來看,選舉中即便再次發生新提案,若是要經過仍是須要acceptor的接受的。那麼P2的變種說法就是:學習
P2a和P2的區別就是在提案那裏加了一個定語,因此其實兩種說法是相同的,由於提案要想被接受,必然須要有大多數的acceptor去接受這個提案。可是存在一種場景,假設一個proposer宕機了,而後重啓後,發佈了一個提案(高編號,可是值不一樣),此時恰好存在一些好死不死的acceptor,也是沒有收到任何的提案,而後這兩個一拍即合的就選擇了這個新提案(根據P1)。可是根據P2,這些acceptor不該該接受這樣的提案,也就衝突了。其實就是缺乏對於proposer的限制。因此須要加強下P2,對實現對proposer的限制(也避免P1和P2之間的衝突):優化
跟P2a很類似,也是針對提案加了定語,可是這裏的issue倒是一個很是重要的過程,論文後面纔會講到,先按下不表。先來講說,這個跟上一個有什麼不一樣。首先,issue是發生在accept前面的,accept的提案必定是issue過了,可是issue的提案就不必定能被accept了(issue的提案只是在第一階段被選中,可是爲了最終一致性可能會捨棄)。P2b->P2a是沒有問題的,並且看起來P2b的限制更強了。那麼提案在issue的時候,就必需要求帶有已經被選中的value這一點,使得壓力不用放在acceptor那裏(由於這裏發送出去的時候,就代表是個已經被大多數acceptor接受的結果,acceptor只要對比本地的結果就能夠最終確認了)。
那麼如何去保證P2b呢,只要在一次paxos提案中,若是已經有一個value被大多數的acceptor接受,那麼後續更高編號的proposal就必須帶有value = v。這個要求每一個在提出新的proposal的proposer,都要收集本次提案中,是否已經有被大多數acceptor接受的提案,有的話,就作個順水人情,幫他完成掉這個提案。那麼就催生了一個新的條件:編碼
簡單來講,那就是,之因此有大多數集的acceptor接受了這個提案,這些acceptor的組成是,(a).歷來沒有接受過比n小的提案,這樣都會接受,就說明這是P1致使的結果,由於歷來沒有接受過提案,那麼n做爲第一個到來的提案,天然能夠接受了。(b).v是編號小於n的提案裏面,被接受的編號最大的那個提案的值,這是爲了知足P2,作個順水人情嘛。到這裏,基本整個算法的流程就算是差很少了。可是到這裏,讀者可能會疑問,好像不知道編號有什麼大的做用的,雖說選大的編號,可是僅僅是上述的說法,不帶編號好像能夠,來看看算法的基本流程就明白了。.net
若是有存在已經接受的提案,請把提案和編號發給我翻譯
這個就是算法的過程,仔細來看,每一個proposer能夠發起兩次請求,而每一個acceptors能夠接收兩次請求,作出兩次迴應。首先對於acceptors來講,acceptor首先能夠無視任何一次請求(也就是超時或者宕機)都不會形成任何安全性的影響。那麼在響應的時候呢,acceptor能夠響應全部的prepare請求(無論是拒絕也好,接受也好)。可是對於accept請求的話,若是已經承諾過不接受的話,那麼就不能相應本身要接受這個請求。也就是說:
一個acceptor只有在沒有接受過其餘編號大於n的prepare請求的狀況下,才能接受一個編號爲n的請求。這個約束是能夠推倒出P1的,由於,若是他沒有接受過任何請求,那麼就能夠接受任何請求了。到這裏,P1,P2兩個約束實際上是相互扶持的P1決定第一次該怎麼作,P2決定後續該怎麼作。
算法到這裏就算是所有結束了,可是這只是理論上的實現,實際編碼則須要作相應的優化,好比編號的產生,好比acceptor在相應的時候反饋一些有用的信息使得proposer能中止本身過期的操做等。可是,這個算法是存在活鎖的問題的。假如如今有兩個proposers,A,B,A先發起了prepare階段並得到了大多數的支持,而後緊接着B帶着更高編號的來了知道A已經得到支持,可是因爲B編號更大能夠得到大多數的支持。緊接着A進入accept階段,發現,你們都接受了更高編號,尷尬了,因而立刻發起新一輪的prepare階段,換了更大編號的。一樣B在進入accept階段的時候也發現了這個問題,因而兩個proposers相互更新編號,即便協議已經達成一致卻一直沒法更新。這就是活鎖問題,論文後續提出,爲了解決活鎖問題,最好引入一個leader proposer,由這個leader來發起提議。可是leader選舉自己也是一個paxos問題。
內容參考:Paxos Made Simple,paxos算法 - 維基百科,csdn的一篇博客