從CAP到Paxos算法


1、背景

 Paxos算法是基於消息傳遞且具有高度容錯特性的一致性算法,是目前公認的解決分布式一致性問題最有效的算法之一,其解決的問題就是在分布式系統中如何就某個值(決議)達成一致。 
在常見的分布式系統中,總會發生諸如機器宕機或網絡異常(包括消息的延遲、丟失、重復、亂序,還有網絡分區)(也就是會發生異常的分布式系統)等情況。Paxos算法需要解決的問題就是如何在一個可能發生上述異常的分布式系統中,快速且正確地在集群內部對某個數據的值達成一致。也可以理解成分布式系統中達成狀態的一致性。

Paxos 算法是分布式一致性算法用來解決一個分布式系統如何就某個值(決議)達成一致的問題。一個典型的場景是,在一個分布式數據庫系統中,如果各節點的初始狀態一致,每個節點都執行相同的操作序列,那么他們最后能得到一個一致的狀態。為保證每個節點執行相同的命令序列,需要在每一條指令上執行一個”一致性算法”以保證每個節點看到的指令一致。
分布式系統中一般是通過多副本來保證可靠性,而多個副本之間會存在數據不一致的情況。所以必須有一個一致性算法來保證數據的一致,描述如下:
  假如在分布式系統中初始是各個節點的數據是一致的,每個節點都順序執行系列操作,然后每個節點最終的數據還是一致的。
  Paxos算法就是解決這種分布式場景中的一致性問題。對於一般的開發人員來說,只需要知道paxos是一個分布式選舉算法即可。多個節點之間存在兩種通訊模型:共享內存(Shared memory)、消息傳遞(Messages passing),Paxos是基於消息傳遞的通訊模型的。

拜占庭問題

拜占庭將軍問題(Byzantine failures),是由paxos的創始人萊斯利·蘭伯特(2013年的圖靈講得主)在1982年提出的,用來為描述分布式系統一致性問題 

故事大概是這么說的:

      拜占庭帝國即中世紀的土耳其,擁有巨大的財富,周圍10個鄰邦垂誕已久,但拜占庭高牆聳立,固若金湯,沒有一個單獨的鄰邦能夠成功入侵。任何單個鄰邦入侵的都會失敗,同時也有可能自身被其他9個鄰邦入侵。拜占庭帝國防御能力如此之強,至少要有十個鄰邦中的6個同時進攻,才有可能攻破。然而,如果其中的一個或者幾個鄰邦本身答應好一起進攻,但實際過程出現背叛,那么入侵者可能都會被殲滅。於是每一方都小心行事,不敢輕易相信鄰國。這就是拜占庭將軍問題。

其實,在拜占庭問題里,各鄰國最重要的事情是:所有國家如何能過達成共識去攻打拜占庭帝國,問題是這些將軍在地理上是分隔開來的,只能依靠通訊員進行傳遞命令,但是通訊員中存在叛徒,它們可以篡改消息,叛徒可以欺騙某些將軍采取進攻行動;促成一個不是所有將軍都同意的決定,如當將軍們不希望進攻時促成進攻行動;或者迷惑某些將軍,使他們無法做出決定。 

在分布式系統領域, 拜占庭將軍問題中的角色與計算機世界的對應關系如下:

(a)將軍, 對應計算機節點;

(b)忠誠的將軍, 對應運行良好的計算機節點;

(c)叛變的將軍, 被非法控制的計算機節點;

(d)信使被殺, 通信故障使得消息丟失;

(e)信使被間諜替換, 通信被攻擊, 攻擊者篡改或偽造信息。

如上文所述,拜占庭將軍問題提供了對分布式共識問題的一種情景化描述,是分布式系統領域最復雜的模型。此外, 它也為我們理解和分類現有的眾多分布式一致性協議和算法提供了框架。現有的分布式一致性協議和算法主要可分為兩類: 

一類是故障容錯算法(Crash Fault Tolerance, CFT), 即非拜占庭容錯算法,解決的是分布式系統中存在故障,但不存在惡意攻擊的場景下的共識問題。也就是說,在該場景下可能存在消息丟失,消息重復,但不存在消息被篡改或偽造的場景。一般用於局域網場景下的分布式系統,如分布式數據庫。屬於此類的常見算法有Paxos算法、Raft算法,、ZAB協議等。

一類是拜占庭容錯算法,可以解決分布式系統中既存在故障,又存在惡意攻擊場景下的共識問題。一般用於互聯網場景下的分布式系統,如在數字貨幣的區塊鏈技術中。屬於此類的常見算法有PBFT算法、PoW算法。

 

 二、CAP理論 

在一個分布式系統中,由於節點故障、網絡延遲等各種原因,根據CAP理論,我們只能保證一致性(Consistency)、可用性(Availability)、分區容錯性(Partition Tolerance中的兩個。

關於分布式系統有一個經典的CAP理論,

CAP

CAP理論的核心思想是任何基於網絡的數據共享系統最多只能滿足數據一致性(Consistency)、可用性(Availability)和網絡分區容忍(Partition Tolerance)三個特性中的兩個。

  • Consistency 一致性
    一致性指“all nodes see the same data at the same time”,即更新操作成功並返回客戶端完成后,所有節點在同一時間的數據完全一致。等同於所有節點擁有數據的最新版本。

  • Availability 可用性

可用性指“Reads and writes always succeed”,即服務一直可用,而且是正常響應時間。
對於一個可用性的分布式系統,每一個非故障的節點必須對每一個請求作出響應。也就是,該系統使用的任何算法必須最終終止。當同時要求分區容忍性時,這是一個很強的定義:即使是嚴重的網絡錯誤,每個請求必須終止。

  • Partition Tolerance 分區容忍性

Tolerance也可以翻譯為容錯,分區容忍性具體指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即系統容忍網絡出現分區,分區之間網絡不可達的情況,分區容忍性和擴展性緊密相關,Partition Tolerance特指在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務

提高分區容忍性的辦法就是一個數據項復制到多個節點上,那么出現分區之后,這一數據項就可能分布到各個區里。分區容忍就提高了。然而,要把數據復制到多個節點,就會帶來一致性的問題,就是多個節點上面的數據可能是不一致的。要保證一致,每次寫操作就都要等待全部節點寫成功,而這等待又會帶來可用性的問題。

可以看到,在分布式系統中,同時滿足CAP定律中“一致性”、“可用性”和“分區容錯性”三者是不可能的。

在通常的分布式系統中,為了保證數據的高可用,通常會將數據保留多個副本(replica),網絡分區是既成的現實,於是只能在可用性和一致性兩者間做出選擇。CAP理論關注的是絕對情況下,在工程上,可用性和一致性並不是完全對立,我們關注的往往是如何在保持相對一致性的前提下,提高系統的可用性。  

如下圖所示,一致性問題,可以根據是否存在惡意節點分類兩類。

無惡意節點,是指節點會丟失、重發、不響應消息,但不會篡改消息。而惡意節點可能會篡改消息。

有惡意節點的問題稱為拜占庭將軍問題,不在今天的討論范圍。Paxos很好地解決了無惡意節點的分布式一致性問題。

 

 三、數據一致性模型

在互聯網領域的絕大多數的場景,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證“最終一致性”,只要這個最終時間是在用戶可以接受的范圍內即可。

對於一致性,可以分為從服務端和客戶端兩個不同的視角,即內部一致性和外部一致性。
沒有全局時鍾,絕對的內部一致性是沒有意義的,一般來說,我們討論的一致性都是外部一致性。外部一致性主要指的是多並發訪問時更新過的數據如何獲取的問題。

強一致性:
當更新操作完成之后,任何多個后續進程或者線程的訪問都會返回最新的更新過的值。這種是對用戶最友好的,就是用戶上一次寫什么,下一次就保證能讀到什么。根據 CAP 理論,這種實現需要犧牲可用性。

弱一致性:
系統並不保證續進程或者線程的訪問都會返回最新的更新過的值。用戶讀到某一操作對系統特定數據的更新需要一段時間,我們稱這段時間為“不一致性窗口”。系統在數據寫入成功之后,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之后可以讀到。

最終一致性:
是弱一致性的一種特例。系統保證在沒有后續更新的前提下,系統最終返回上一次更新操作的值。在沒有故障發生的前提下,不一致窗口的時間主要受通信延遲,系統負載和復制副本的個數影響。

最終一致性模型根據其提供的不同保證可以划分為更多的模型,包括因果一致性和讀自寫一致性等。

四、兩階段和三階段提交

在分布式系統中,各個節點之間在物理上相互獨立,通過網絡進行溝通和協調。
典型的比如關系型數據庫,由於存在事務機制,可以保證每個獨立節點上的數據操作可以滿足ACID。
但是,相互獨立的節點之間無法准確的知道其他節點中的事務執行情況,所以兩台機器理論上無法達到一致的狀態。

如果想讓分布式部署的多台機器中的數據保持一致性,那么就要保證在所有節點的數據寫操作,要不全部都執行,要么全部的都不執行。
但是,一台機器在執行本地事務的時候無法知道其他機器中的本地事務的執行結果。所以節點並不知道本次事務到底應該commit還是 roolback。

所以實現分布式事務,需要讓當前節點知道其他節點的任務執行狀態。常規的解決辦法就是引入一個“協調者”的組件來統一調度所有分布式節點的執行。著名的是二階段提交協議(Two Phase Commitment Protocol)和三階段提交協議(Three Phase Commitment Protocol)。

4.1二階段提交協議

Two Phase指的是Commit-request階段Commit階段。

  • 請求階段
    在請求階段,協調者將通知事務參與者准備提交或取消事務,然后進入表決過程。
    在表決過程中,參與者將告知協調者自己的決策:同意(事務參與者本地作業執行成功)或取消(本地作業執行故障)。

  • 提交階段
    在該階段,協調者將基於第一個階段的投票結果進行決策:提交或取消。
    當且僅當所有的參與者同意提交事務協調者才通知所有的參與者提交事務,否則協調者將通知所有的參與者取消事務。參與者在接收到協調者發來的消息后將執行響應的操作。

2PC Commit

可以看出,兩階段提交協議存在明顯的問題:

  • 同步阻塞
    執行過程中,所有參與節點都是事務獨占狀態,當參與者占有公共資源時,第三方節點訪問公共資源被阻塞。

  • 單點問題
    一旦協調者發生故障,參與者會一直阻塞下去。

  • 數據不一致性
    在第二階段中,假設協調者發出了事務commit的通知,但是因為網絡問題該通知僅被一部分參與者所收到並執行commit,其余的參與者沒有收到通知一直處於阻塞狀態,這段時間就產生了數據的不一致性。

2.三階段提交協議

Three Phase分別為CanCommit、PreCommit、DoCommit。

3PC Commit

三階段提交針對兩階段提交做了改進:

  • 引入超時機制。在2PC中,只有協調者擁有超時機制,3PC同時在協調者和參與者中都引入超時機制。
  • 在第一階段和第二階段中插入一個准備階段。保證了在最后提交階段之前各參與節點的狀態是一致的。

四、Paxos算法的提出

二階段提交還是三階段提交都無法很好的解決分布式的一致性問題,直到Paxos算法的提出,Paxos協議由Leslie Lamport最早在1990年提出,目前已經成為應用最廣的分布式一致性算法。

Google Chubby的作者Mike Burrows說過這個世界上只有一種一致性算法,那就是Paxos,其它的算法都是殘次品。 

Paxos算法的前提假設是不存在拜占庭將軍問題,即:信道是安全的(信道可靠),發出的信號不會被篡改,因為Paxos算法是基於消息傳遞的。此問題由Lamport提出,它也是 Paxos算法的提出者。
從理論上來說,在分布式計算領域,試圖在異步系統和不可靠信道上來達到一致性狀態是不可能的。因此在對一致性的研究過程中,都往往假設信道是可靠的,而事實上,大多數系統都是部署在一個局域網中,因此消息被篡改的情況很罕見;另一方面,由於硬件和網絡原因而造成的消息不完整問題,只需要一套簡單的校驗算法即可。因此,在實際工程中,可以假設所有的消息都是完整的,也就是沒有被篡改。

在這里插入圖片描述

Paxos算法的相關概念
在Paxos算法中,有三種角色:

    •  Proposer
    •  Acceptor
    •  Learners
      在具體的實現中,一個進程可能同時充當多種角色。比如一個進程可能既是Proposer又是Acceptor又是Learner。Proposer負責提出提案,Acceptor負責對提案作出裁決(accept與否),learner負責學習提案結果。
      還有一個很重要的概念叫提案(Proposal)。最終要達成一致的value就在提案里。只要Proposer發的提案被Acceptor接受(半數以上的Acceptor同意才行),Proposer就認為該提案里的value被選定了。Acceptor告訴Learner哪個value被選定,Learner就認為那個value被選定。只要Acceptor接受了某個提案,Acceptor就認為該提案里的value被選定了。
      為了避免單點故障,會有一個Acceptor集合,Proposer想Acceptor集合發送提案,Acceptor集合中的每個成員都有可能同意該提案且每個Acceptor只能批准一個提案,只有當一半以上的成員同意了一個提案,就認為該提案被選定了。
    • 在這里插入圖片描述
      • Paxos算法的解決的問題描述
        假設有一組可以提出(propose)value(value在提案Proposal里)的進程集合。一個一致性算法需要保證提出的這么多value中,只有一個value被選定(chosen)。如果沒有value被提出,就不應該有value被選定。如果一個value被選定,那么所有進程都應該能學習(learn)到這個被選定的value。對於一致性算法,安全性(safaty)要求如下:
      •  只有被提出的value才能被選定。
      •  只有一個value被選定,並且如果某個進程認為某個value被選定了,那么這個value必須是真的被選定的那個。
        Paxos的目標:保證最終有一個value會被選定,當value被選定后,進程最終也能獲取到被選定的value 

1.節點角色

Paxos 協議中,有三類節點:

  • Proposer:提案者

Proposer 可以有多個,Proposer 提出議案(value)。所謂 value,在工程中可以是任何操作,例如“修改某個變量的值為某個值”、“設置當前 primary 為某個節點”等等。Paxos 協議中統一將這些操作抽象為 value。
不同的 Proposer 可以提出不同的甚至矛盾的 value,例如某個 Proposer 提議“將變量 X 設置為 1”,另一個 Proposer 提議“將變量 X 設置為 2”,但對同一輪 Paxos 過程,最多只有一個 value 被批准。

  • Acceptor:批准者

Acceptor 有 N 個,Proposer 提出的 value 必須獲得超過半數(N/2+1)的
Acceptor 批准后才能通過。Acceptor 之間完全對等獨立。

  • Learner:學習者

Learner 學習被批准的 value。所謂學習就是通過讀取各個 Proposer 對 value 的選擇結果,如果某個 value 被超過半數 Proposer 通過,則 Learner 學習到了這個 value。

這里類似 Quorum 議會機制,某個 value 需要獲得 W=N/2 + 1 的 Acceptor 批准,Learner 需要至少讀取 N/2+1 個 Accpetor,至多讀取 N 個 Acceptor 的結果后,能學習到一個通過的 value。

2.約束條件

上述三類角色只是邏輯上的划分,實踐中一個節點可以同時充當這三類角色。

Paxos中 proposer 和 acceptor 是算法的核心角色,paxos 描述的就是在一個由多個 proposer 和多個 acceptor 構成的系統中,如何讓多個 acceptor 針對 proposer 提出的多種提案達成一致的過程,而 learner 只是“學習”最終被批准的提案。

Paxos協議流程還需要滿足幾個約束條件:

  • Acceptor必須接受它收到的第一個提案;
  • 如果一個提案的v值被大多數Acceptor接受過,那后續的所有被接受的提案中也必須包含v值(v值可以理解為提案的內容,提案由一個或多個v和提案編號組成);
  • 如果某一輪 Paxos 協議批准了某個 value,則以后各輪 Paxos 只能批准這個value;

每輪 Paxos 協議分為准備階段和批准階段,在這兩個階段 Proposer 和 Acceptor 有各自的處理流程。

Proposer與Acceptor之間的交互主要有4類消息通信,如下圖:

paxos-message

這4類消息對應於paxos算法的兩個階段4個過程:

  • Phase 1 a) proposer向網絡內超過半數的acceptor發送prepare消息 b) acceptor正常情況下回復promise消息
  • Phase 2 a) 在有足夠多acceptor回復promise消息時,proposer發送accept消息 b) 正常情況下acceptor回復accepted消息

3.選舉過程

選舉過程

  • Phase 1 准備階段

Proposer 生成全局唯一且遞增的ProposalID,向 Paxos 集群的所有機器發送 Prepare請求,這里不攜帶value,只攜帶N即ProposalID 。

Acceptor 收到 Prepare請求 后,判斷:收到的ProposalID 是否比之前已響應的所有提案的N大:
如果是,則:
(1) 在本地持久化 N,可記為Max_N。
(2) 回復請求,並帶上已Accept的提案中N最大的value(若此時還沒有已Accept的提案,則返回value為空)。
(3) 做出承諾:不會Accept任何小於Max_N的提案。

如果否:不回復或者回復Error。

  • Phase 2 選舉階段

P2a:Proposer 發送 Accept
經過一段時間后,Proposer 收集到一些 Prepare 回復,有下列幾種情況:
(1) 回復數量 > 一半的Acceptor數量,且所有的回復的value都為空,則Porposer發出accept請求,並帶上自己指定的value。
(2) 回復數量 > 一半的Acceptor數量,且有的回復value不為空,則Porposer發出accept請求,並帶上回復中ProposalID最大的value(作為自己的提案內容)。
(3) 回復數量 <= 一半的Acceptor數量,則嘗試更新生成更大的ProposalID,再轉P1a執行。

P2b:Acceptor 應答 Accept
Accpetor 收到 Accpet請求 后,判斷:
(1) 收到的N >= Max_N (一般情況下是 等於),則回復提交成功,並持久化N和value。
(2) 收到的N < Max_N,則不回復或者回復提交失敗。

P2c: Proposer 統計投票
經過一段時間后,Proposer 收集到一些 Accept 回復提交成功,有幾種情況:
(1) 回復數量 > 一半的Acceptor數量,則表示提交value成功。此時,可以發一個廣播給所有Proposer、Learner,通知它們已commit的value。
(2) 回復數量 <= 一半的Acceptor數量,則 嘗試 更新生成更大的 ProposalID,再轉P1a執行。
(3) 收到一條提交失敗的回復,則嘗試更新生成更大的 ProposalID,再轉P1a執行。

4.相關討論

Paxos算法的核心思想:
(1)引入了多個Acceptor,單個Acceptor就類似2PC中協調者的單點問題,避免故障
(2)Proposer用更大ProposalID來搶占臨時的訪問權,可以對比2PC協議,防止其中一個Proposer崩潰宕機產生阻塞問題
(3)保證一個N值,只有一個Proposer能進行到第二階段運行,Proposer按照ProposalID遞增的順序依次運行
(3) 新ProposalID的proposer比如認同前面提交的Value值,遞增的ProposalID的Value是一個繼承關系

為什么在Paxos運行過程中,半數以內的Acceptor失效都能運行?
(1) 如果半數以內的Acceptor失效時 還沒確定最終的value,此時,所有Proposer會競爭 提案的權限,最終會有一個提案會 成功提交。之后,會有半過數的Acceptor以這個value提交成功。
(2) 如果半數以內的Acceptor失效時 已確定最終的value,此時,所有Proposer提交前 必須以 最終的value 提交,此值也可以被獲取,並不再修改。

如何產生唯一的編號呢?
在《Paxos made simple》中提到的是讓所有的Proposer都從不相交的數據集合中進行選擇,例如系統有5個Proposer,則可為每一個Proposer分配一個標識j(0~4),則每一個proposer每次提出決議的編號可以為5*i + j(i可以用來表示提出議案的次數)。

推薦larmport和paxos相關的三篇論文:
The Part-Time Parliament
Paxos made simple
Fast Paxos

2PC/3PC和Paxos協議是經典的分布式協議,理解了它們以后,學習其他分布式協議會簡單很多。

參考:從CAP理論到Paxos算法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM