paxos-分散式系統資料一致性演算法學習

東邊的小山發表於2017-11-15

Paxos演算法是萊斯利·蘭伯特(Leslie Lamport,此人現在在微軟研究院)於1990年提出的一種基於訊息傳遞的一致性演算法。這個演算法被認為是類似演算法中最有效的。文章中很多東西借鑑大神的部落格,在最下面有連結。該文章只是自己加深印象,做個總結。

一:基本語義

在Paxos演算法中有一下幾種角色:

Proposer:議案的提議者

Acceptor:議案的決議者

Client:發出議案者

Learner:最終議案的學習者

以上四種角色,議案的提議者和決策者最重要,Proposer通俗一點就是Client的使者,Client提出一個議案,然後交於Proposer,Proposer再拿著這個議案去向Acceptor申請。還有一個名詞就是最終決議,稍後再介紹。

還有一下幾種關鍵字:

Proposal:議案,由proposer提出,最終被acceptor批准或者否決

Value:決議,他是議案的內容,一個議案就是一個{編號,決議}對

Accept:批准,表示議案被Acceptor批准

Choose:選擇,表示議案被選擇,也就是被多數Acceptor批准

演算法一致性的基本語義:

1):決議(value)只有在被Proposer提出之後才能被批准成為議案(議案是Proposal)

2):再一次Poaxs演算法執行示例中,有且僅有一個value被choosen

3):Learner只能學習最終被批准的value

以上三個語義可以轉化為四個約束條件:

P1:一個Acceptor必須接受(accpet)第一次收到的議案

        但是該需求可能會導致一個問題,如果多個Proposer分別提出幾個不同的提議,從而導致每個Acceptor 分 別批註了幾個不同的提議,但是沒有一個提議被多數派接受。即使只有兩個決議,但是有可能有這種情 況,就是在單個Acceptor失效的情況下,每個議案都被半數的Acceptor接受,那麼還是沒有辦法提出最終的議案。

        一個決議要經過多數派的批准猜中最終被choose,這個需求和P1暗示了我們Acceptor必須能夠批准多個議案,因此我們為每個不同的議案分配不同的編號(如果只有三個Proposer,我們可以給他們編號0,1,2,那麼他們的議案編號就可以是3*i+j,其中i可以用來跟蹤提出議案的次數,j是他們開始的編號,這樣就可以做到唯一遞增,同理,多個也是),因此每一個議案由編號和決議構成,也就是【議案={編號,決議}】,如果一個議案被多數派Accept,那麼他就被choose,我們允許選擇多個議案,但是必須保證所有選擇的議案都包括相同的決議,歸納如下:

P2. 如果一個議案{n, v}被選擇,那麼所有被選擇的議案(編號更高)包含的決議都是v。 

因為編號是全序的,P2保證了“有且僅有一個決議被選擇”這一關鍵屬性,議案必須被至少一個Acceptor批准才能被選擇,因此只要滿足一下條件,就能滿足P2:

P2A:一旦一個編號n,value v({n,v})的議案被choose, 那麼之後任何Acceptor再次接受的議案(編號更高)必須具有value v

依然根據P1來確認選擇了某些議案。因為通訊是非同步的,所以可能在有些情況下,某些Acceptor可能沒有接受過一些議案,他們可能會錯誤的批准一個議案,試想一下,加入某個Proposer以內某些原因down了,但是後續自己又啟動了,他提出了一個編號更高的議案,那麼根據P21,Acceptor應該去批准這個議案,但是又違背了P2A,所以我們要去加強P2A:

P2B:一旦一個編號n,value v({n,v})的議案被choose,那麼之後任何Proposer提出的議案(編號更高)必須具有value v

因為一個議案只有在被Proposer提出之後才有可能被批准,因此P2B包含了P2A,進而包含了P2

但是如何才能證明P2B,假設某個議案{m,v}被選擇,然後我們可以去證明任何n>m的議案的決議都是v,對n可以簡化證明:根據條件,每個提出的議案(編號m到n-1)他的決議都是v,我們可以證明編號為n的決議是v,對於選擇的議案m,必然存在一個集合c(Acceptor的多數派)中的多數Acceptor已經批准了該議案,綜合假設歸納,m被選擇這一前提意味著:

C中的每個Acceptor都批准了一個一個編號m到n-1範圍內的議案,並且議案的決議都是v

因為任何多數派組成的集合都包括至少C中的一員,那麼我們可以得出結論,如果下面的不變性成立,那麼議案n的決議就是v

P2C:對於任意的v和m,如果議案{n,v}被提出,那麼存在一個有多數派構成的結合s(或者a,s中沒有沒有acceptor批准過小於編號n的議案,或者b)在s中的acceptor批准的所以議案(編號小於n)中,v編號小於n的提案的最大決議

通過保持P2C,我們就能滿足P2B,為了保持P2C,準備提出提案(編號為n)的Proposer必須知道所有編號小於n的議案中編號最大的那個,如果存在的話,他可能已經或者將要被Acceptor多數派批准,獲取已經批准的議案是簡單的,但是預測將要批准的議案確實困難的,Proposer並不預測,它假設不會有這種情況,因此他會硬性規定Acceptor不許批准任何編號小於n的議案,這就引起了下面的基本演算法也就是我們說的兩階段提交。

1):Proposer選擇了一個新的編號n,準備向Acceptor集合中所有成員傳送請求(Prepare階段,n是Prepare請求的編號,也是下面accept回應議案的編號),並且要求回應:

    a、一個永不批准編號小於n的議案的承諾

    b、在他已經批准的編號小於n的議案中,編號最大的議案,如果存在的話,我們把這樣的請求叫做prepare請求n

2):如果Proposer收到多數Acceptor的回應,那麼他就可以提出議案{n,v}其中v是所有回應中編號最高的議案的決議,或者是Proposer選擇的任意值,前提是Acceptor從未批准過任何議案

一個Proposer發出一個Acceptor集合傳送已經被批准的議案,我們稱之為Accept請求

換言之:就是

P1A:Acceptor可以批准一個編號為n的議案,當且僅當他沒有回應過一個編號大於n的Prepare請求

P1A包含了P1,現在我們得到了一個一致性演算法,假設滿足安全性,假設議案的編號唯一,我們就可以通過簡單的優化,得到下面一致性演算法:

假設一個Acceptor接收到一個編號為n的Prepare請求,但是他已經回應了一個編號大於n的Prepare請求,於是Acceptor就沒有必要回應這個Prepare請求了,因為他不會這個批准這個編號為n的議案,他還可以忽略已經批准過的議案的Prepare請求。

有了這些優化,Acceptor只需要儲存它已經批准過的最高編號的議案(議案的編號和決議),以及他已經回應過得所有Prepare請求的最高編號,因為任何情況下,都需要保證P2C,Acceptor都需要儲存這些資訊,包括重啟之後,注意,Proposer可以隨意拋棄一個提案,但是他不會用相同的編號來提出另一個議案。

二:基本演算法

其中通訊模型我們採用非同步的非拜占庭模型【拜占庭模型下,訊息會丟失,重複,也有可能會損壞,換言之:我們認為訊息可能會丟失,也可能會重複,但是訊息不會出現損壞的現象】

下面簡單介紹下Paxos演算法的一些行為,基本分為兩段提交過程:

1)Prepare階段:

        (1):當Proposer希望提出一個議案V1,首先傳送Prepare請求到大多數的Acceptor,Prepare請求的序列號為<SN1>;

         (2):當Acceptor接收到編號為<SN1>的Prepare請求的時候,他首先回去檢查自身上次回覆過得Prepare請求<SN2>

                a):如果SN2>SN1,忽略該請求,並且告知提出<SN1>議案的Proposer,他已經回覆過一個編號大於SN1的Prepare請求,

                b):否則去檢查上次批准的Prepare請求<SNx,Vx>,並且回覆<SNx,Vx>;如果該Acceptor之前沒有回覆過,那麼直接回復<OK>

2)Accept階段 :

         (1):   因為通訊是非同步的,所以我們可以在經過一段時間後,Proposer收到Acceptor的請求,回覆可以分為幾種:

            a):回覆的數量滿足多數派,而且回覆的都是<OK>,那麼Proposer發起Accept請求,內容就是<SN1,V1>

            b):回覆的數量滿足多數派,但是有的回覆<SN2,V2>,<SN3,V3>.....則,Proposer找到所有回覆中超過半數的那個,假如是<SNx,Vx>,則發出Accept請求,內容為<SN1,Vx>,此時他的編號不變,但是議案的決議變成Acceptor回覆中超過半數議案的決議Vx

            c):回覆的數量不滿足多數派,那麼Proposer會嘗試編號+1再次發出Prepare請求,

         (2):在不違背自己向其他Proposer的承諾前提下,acceptor收到accpet請求後既接受並回復這個請求。

三:演算法優化

但是在應用場景下,很容易出現活鎖的情況,例如當三個或三個以上的Proposer在傳送Prepare請求,很難有一個Proposer收到超過半數的回覆而不停的執行第一階段協議,這就陷入了一個怪圈,因此為了避免競爭,加快收斂速度,在演算法中引入了一個Leader的角色,在正常情況下有且僅有一個參與者扮演leader角色,除非leader down掉或者是他丟掉了半數之上的Acceptor,才會重新選舉leader。而且他角色扮演Acceptor,同時又扮演Learner角色。

在這種優化演算法中,只有Leader才能提出議案,從而避免了競爭使得演算法快速收斂而趨於一致,此時的Paxos演算法在本質上退變為兩階段提交協議。但是在異常情況下,可能出現了多個leader,此時演算法有變成了原始的Paxos演算法。

Leader的選舉也是用到了兩階段提交協議。

此時Leader的工作流程主要分為以下三部分:

1、學習階段 向其他參與者學習自己不知道的資料(決議)

當一個參與者經過選舉後成為了leader,他應該知道絕大多數的Paxos的例項,因此他會馬上啟動一個主動學習的過程。假設當前的新Leader早就知道1-134,138和139的Paxos例項,那麼他就會執行137-137以及大於139的paxos。如果只檢測到135和140的Poxas例項有確定的值,那他最後就會知道1-135,138-140的paxos的例項。

2、同步階段 讓絕大多數的參與者保持資料(決議)的一致性

此時的Leader已經知道1-134和138-140的poxas例項,那麼他會重新執行1-135的例項,以保證絕大多數參與者在1-135的paxos例項上保持一致。對於138-140的例項,他不會馬上去執行,而是等到服務階段填充了136,137的paxos例項後在執行。這裡之所以要填充這些例項,是為了防止以後Leader總是去學習這些間隔的例項,但是這些例項有沒有確定的值,就會造成一定程度的資源浪費。

3、服務階段 為客戶端服務,提議案

 Leader將使用者的請求轉化為Paxos例項,當然,他可以去執行多個Paxos例項,但是如果當前leader出現異常,就有可能出現Paxos例項間斷的問題,或者在異常情況下,出現了多個leader,此時Paxos演算法就有可能出現活鎖的現象。

連結:http://blog.csdn.net/sparkliang/article/details/5740882

連結:http://blog.csdn.net/xhh198781/article/details/10949697

連結:http://shuofenglxy.iteye.com/blog/1188422

相關文章