分布式一致性(共识)算法(Paxos,raft,ZAB)的一些总结
文章目錄
- 前言
- CAP理論
- C consistency 一致性
- A availability 可用性
- P partition tolerance 分區容錯性
- 一致性模型
- 弱一致性
- 強一致性
- 強一致性算法
- 需要明確的問題
- 強一致算法: 主從同步
- 強一致性算法:多數派
- 強一致算法:Paxos
- Basic Paxos
- Multi Paxos
- 第一個版本:使用Proposer表示唯一的一個Leader
- 第二個版本:將算法角色進一步簡化
- 強一致算法: Raft(基于log replicated的共識算法)
- 強一致算法:ZAB
- 總結
前言
分布式系統是當前互聯網領域的一個重要且大勢所趨的一種解決計算,存儲,網絡相關問題的方向,利用數量龐大的服務器節點作為底層,在其之上搭建一個可以跨省甚至跨國的龐大系統。
它能夠保證用戶數據的一致性,對用戶提供服務的高可用性,以及跨地域的分區容錯性,而分布式一致性算法就是為了保證分布式系統能夠滿足其特性。
分布式一致性算法,我將其劃分到了分布式存儲技能樹中的分布式理論中:
本文的總結,將按照以下導圖進行知識點的總結和梳理
CAP理論
提到分布式系統,那么CAP理論便無法繞過了。自從2000年,Eric Brewer教授提出了分布式系統設計一定會遵循:一致性,可用性,分區容錯性的三個原則之后,互聯網界的分布式系統便都將其作為自己的衡量標準。后續的分布式系統設計也都按照CAP理論進行設計,但是任何一個系統都只能在三個原則中滿足兩個,無法三個都滿足,即使像最近的亞馬遜S3和google spanner等全球分布式系統也只能滿足CP兩種,對于A則也只能提供多個9的可用性。
后續該理論上升為定理之后也有相關的證明論文,本節只是將其作為一個基本理論掃盲的介紹。
C consistency 一致性
在分布式系統中有多個節點,整個系統對外提供的服務應該是一致的。即用戶在不同的系統節點訪問數據的時候應該是同樣的結果,不能出現1號節點是結果1, 2號節點是結果2這種不一致的情況。
A availability 可用性
分布式系統為用戶提供服務,需要保證能夠在一些節點異常的情況下仍然支持為用戶提供服務。
P partition tolerance 分區容錯性
分布式系統的部署可能跨省,甚至跨國。不同省份或者國家之間的服務器節點是通過網絡進行連接,此時如果兩個地域之間的網絡連接斷開,整個分布式系統的體現就是分區容錯性了。
在這種系統出現網絡分區的情況下系統的服務就需要在一致性 和 可用性之間進行取舍。
如果為了在出現網絡分區之后提供仍然能夠提供可用性,那么一致性必然無法滿足。因為可用性的要求是系統仍然能夠提供服務,但是這個時候分布式系統在兩個地域之間無法通信,那么America的請求無法同步到China的服務節點上,整個系統的一致性顯然不能滿足。
同理 為了保證一致性,那么可用性也必然不能滿足,因為一致性的要求是請求A寫入America之后從China中讀,一定要能夠讀到這個請求(強一致性)或者 經過一段時間之后也能夠讀到(弱一致性)。但是兩地域已經出現了網絡分區,那么請求會阻塞從而降低可用性。
所以CAP理論一定是無法全部滿足三者,只能滿足其中的兩者。
一致性模型
弱一致性
弱一致性體現的是最終一致性,即如上CAP理論中 ,兩個地域的請求分別通過兩地的節點寫入,可以不用立即進行同步,而是經過一段時間之后兩地的用戶數據變為一致。這種一致性成為弱一致性,即最終一致性。
也就是用戶一地寫入之后從另外一個節點讀取,無法立即讀到剛才寫入的數據。
比如DNS域名系統,Cassandra的 Gossip協議等都是最終一致性的體現。
全球目前的域名系統可以看作一個域名樹,由一個根域名節點.cn下轄多個.edu,.org,.com等子域名節點,而這一些子域名之下🈶?更多的子域名(以企業為例www.kuaishou.com,之下有多個數據中心,不同的數據中心又數百上千的存儲節點),當我們向某一個節點更新域名信息的時候,在其他節點立即訪問該記錄則不一定立即訪問到最終的更新,而是經過一段時間之后我們能夠看到更新。
強一致性
強一致性是非常有必要的,比如業界的銀行系統,支付系統等對一致性要求非常高。
當下比較流行的強一致性算法 像 Paxos,以及基于paxos衍生的raft,ZAB都能提供強一致性的能力。
強一致性描述的是一個請求在一個節點寫入之后在其他節點讀取,則該數據的更新能夠被讀到。
接下來主要描述的是一些強一致性算法的原理。
強一致性算法
需要明確的問題
- 數據不能存放在單個節點上,當然這個是分布式系統的基礎形態,不滿足這個分布式系統也不能稱為分布式系統了
- 分布式系統對分區容錯的方案是 state machine replication (復制狀態機),而強一致算法也被稱為共識算法(consensus)
state狀態機 簡單來說就是一個函數,根據輸入的不同值來執行對應的請求處理邏輯。
為了保證系統的最終一致性,不僅需要系統內部達成共識,也需要一些client的行為。
強一致算法: 主從同步
主從同步復制:
master接受寫請求master復制日志到slavemaster等待直到所有從節點返回
這是一個強一致性算法的基礎,但是這樣的模型會出現如下問題:
比如一個slave節點異常宕機,那么master遲遲等不到該節點的返回,此時client請求便會阻塞。這樣的模型就是強一致模型,但是系統的可用性就會非常差,一個節點掛掉會導致整個系統不可用。
強一致性算法:多數派
為了避免主從同步那樣一個節點異常,整個集群不可用的情況,推出了多數派。即每次寫入只需要保證寫入節點數大于N/2 個,讀節點數保證大于N/2個節點,此時即可認為能夠向客戶端返回了。
這樣既能夠保證一致性,又提高了可用性。但是這個情況也只是針對單個請求寫入的情況下,而我們實際生產環境中會存在大量的并發場景。
比如:
并發這個分布式系統的不同節點執行不同的操作,在滿足多數派的情況下可能會出現順序的問題,不同的節點因為請求同步的時間問題可能出現請求的順序不一致。
強一致算法:Paxos
基礎的Paxos算法包括如下三種:
- Basic Paxos
- Multi Paxos
- Fast Paxos
paxos算法是Lasile Lamport的發明,這個算法已經有很多論文對其基本原理進行描述,因為本身角色很多又要在不同的異常場景下描述清楚,很難真正讓讀者對該算法融會貫通的。
Lamport為了更加形象的描述,提出了虛擬出來一個叫做Paxos的希臘城邦,這個城邦的管理者為了方便對民眾的管理,提出了一系列法案用來對民眾進行約束,當民眾有民事請求時,會提交民事訴訟給國會,有一個組織者將該訴訟整理成法案交給一個個議員進行處理,議員之間又分為投票者和接受者用來實際進行該法案最終是否要處理的決定,最終將結果反饋給組織者,由組織者將最終的結果再返還給民眾。
具體的角色如下:
Client:系統外部角色,請求的發起者。就像是故事中的民眾Proposer: 接受Client請求,想集群提出提議(propose)。并在投票的國會發生沖突的時候進行調節,經結果反饋給民眾Accpetor(Voter): 投票者,就是負責在集群內部對法案是否要寫入進行投票。只有滿足多數派(法定人數Quorum的由來)的情況下提議才會被接受,像是國會。Learner:接受者,即進行backup,將民眾的請求記錄下來。這個角色對集群一致性本身并沒有什么影響。
Basic Paxos
主要分為如下幾個階段(phases):
Phase 1a: Prepare階段
這個階段Proposer提出一個提案編號為N;此時N大于這個Proposer之前提出的議案,請求這個集群的AcceptorQuorum接受。Phase 1b: Promise階段
如果N大于此Acceptor之前接受的任何提案編號則接受,否則拒絕。Phase 2a: Accept階段
如果Acceptor內部投票通過的人數達到了多數派,則Proposer會發出accept的請求,該請求包括提案編號為N,以及提案的內容(用戶請求的內容)Phase2b: Accepted階段
如果此acceptor在此期間沒有收到任何編號大于N的提案,則接受此提案內容,否則忽略。接受之后會向Proposer返回成功,且將議案的內容交給Learner進行backup
正常的處理流程如下:
客戶端發起request請求到Proposer,然后Proposer發出提案并攜帶提案的編號1,分別發給每一個Acceptor;每一個Acceptor根據天都編號是否滿足大于1,將投票結果通過Propose階段反饋給Proposer;Proposer收到Acceptor的結果判斷是否達到了多數派,達到了多數派則向Acceptor發出Accept請求,并攜帶提案的具體內容V;Acceptor收到提案內容后向Proposer反饋Accepted表示收到,并將提案內容交給Learner進行備份。
Learner寫入提案內容之后向Client發送完成請求的響應。
當Acceptor出現異常時流程如下:
雖然Accetor3 出現異常,沒有向Proposer反饋,但是Proposer此時收到的接受提案的反饋有2個Acceptor,仍然滿足多數派的情況,此時仍然能夠將提案內容繼續寫入的,所以后續的Accept的發送只需要發送給剩下的兩個Acceptor即可。
當Proposer出現異常時的情況如下:
Proposer失敗的話表示收到Acceptor的Propose請求之后無法繼續發送Accept請求,這個時候集群會重新選出另一個新的能夠工作的Proposer,再從prepare階段開始處理,同時Prepare的提案版本號會增加一個,但是提案的內容還是之前的內容。
當出現活鎖的情況:
簡化了如下處理流程,當然其中的 Proposers和Acceptors 不只一個,是由多個組成的。
基本情況就是集群中有多個Propser,當proposer1發送prepare版本為1并收到propose的時候節點發生了異常,集群切換到了新的proposer,并重新prepare 版本2,準備好和版本1相同的內容的提案。(因為acceptor處理的過程中發現更高版本的提案,會丟棄當前的版本,轉向更高版本去處理)。
當proposer2等待acceptor的propose返回時,proposer1有上線了,發現自己prepare(1)提案被打斷,此時又準備了一個更高版本的prepare(3)提案,打斷了proposer2的2版本提案;當proposer2發現自己的2號版本被打斷,又準備了更高的4號版本,從而打斷了propose1的3號提案版本;依此下去,整個集群將會阻塞在相同的提案的不斷提交之中,這種情況就是集群出現了活鎖。
當然也有較好的解決措施,比如:proposer1的上線之后 重新提交法案使用隨機時間機制,即隨機生成一個時間戳,在這段時間內不向Acceptor發送消息;這樣proposer2的提案能夠被處理完成,這個時候proposer1再次提交新的提案。
綜上這一些基本異常情況來看,basic paxos還是能夠保證系統的一致性和可用性,共識算法還是能夠正常工作。
但是basic還是有一些缺點:
- 難實現
有太多的角色需要實現 - 效率低(2輪 rpc)
每一個請求需要proposer和acceptor 至少交互兩次才能完成請求的處理 - 活鎖
這個時候推出了Multi Paxos,其中的特色就是Leader概念,Leader是由唯一的Proposer表示,所有的請求都需要經過這個Leader進行轉發。
Multi Paxos
第一個版本:使用Proposer表示唯一的一個Leader
Proposer可以直接用來做提案是否被接受的決定,而不用所有的acceptor各自決定是否要接受提案。
正常處理流程如下:
- 處理第一個請求的時候因為需要選擇leader,所以還是會走2輪rpc,從prepare 到最后的Accepted。
- 從第二個請求開始,所有的請求只需要交給Proposer,Proposer即可完成是否接受請求的決定,一次Accept即可達成將請求寫入Learner的過程。
第二個版本:將算法角色進一步簡化
這個簡化之后便更加接近我們實際的分布式系統的情況
當然這個還是正常請求的處理流程,不過只會通過一輪Accept通信即可完成請求的處理。
ps:簡化后的集群第一個請求還是會走2輪rpc,為了從servers中選舉出一個leader,這個過程還是會從prepare開始到Accepted完成。
強一致算法: Raft(基于log replicated的共識算法)
raft是更為簡化的multi paxos(其實也就是上一個圖中的paxos)算法,相比于paxos的復雜實現來說角色更少,問題更加精簡。
rafti整體來說可以拆分成三個子問題來達成共識:
Leader Election如何選擇出leaderLog Replication如何將log復制到其他的節點Safety保證復制之后集群的數據是一致的
重新定義了新的角色:
Leader一個集群只有一個leaderFollower一個服從leader決定的角色Cadidatefollower發現集群沒有leader,會重新選舉leader,參與選舉的節點會變成candidate
除了以上的精簡之外,raft還提供了完善的社區 甚至 完整共識過程的可視化展示,這里可以通過原理動畫展示 : http://thesecretlivesofdata.com/raft/ 瀏覽。
主體過程如下:
-
三個基本的節點角色介紹一下
-
Leader Election的選舉過程
初始所有節點都是follower狀態,當開始選舉的時候將待選舉節node a點置為candidate狀態
canditdate會向其他的follower節點發送請求進行leader投票,其他節點投票通過,則將candidate節點的狀態置為leader狀態。
接下來通過兩種心跳時間來詳細看一下選舉過程的實現
在leader election選舉的過程中會有兩種timeout的設置來控制整個的選舉過程:
-
Election timeout
表示follower等待請求的超時時間,如果這個時間結束前還沒有收到來自leader或者選舉leader的請求,那么當前follower便會變為candidate。 raft給的設定值一般在150ms-300ms之間的隨機值。
變成candidate之后,當前節點會立即發送leader的投票請求,其他的follower節點會重置選舉的超時時間。 -
heartbeat timeout
新選舉出來的leader每隔一段時間會發送一個請求給follower,這個時間就是心跳時間。
同樣follower在相同的時間間隔內回復leader一個請求,表示自己已經收到。這樣的心跳間隔將會一直持續,直到一個follower停止接受心跳,變成candidate。
重新選舉的過程就是candidate發送選舉請求,follower接受到之后返回對應的心跳回應,candidate根據心跳回應的個數判斷是否滿足多數派,從而變成leader。變成leader之后,會持續發送心跳包來保證follower的存活。
Log Replication過程
主要過程如下:
客戶端發送請求到leader,leader接受之后將請求封裝成log entry,并將log副本發送給其他的follower節點。
等待其他的follower節點回復,發現達到了多數派之后leader便將該entry寫入到自己的文件系統之中;
寫入之后再發送請求,表示follower節點也可以寫入了。
接下來我們詳細看看log Replicated的實現過程,我們的leader被選舉出來之后用于請求的轉發,將接受到的用戶請求封裝成log entry,并將log entry的副本轉發給follower,這個log enry發送到follower之后也會用于重置follower的心跳時間。- 客戶端會發送一條請求到leader,這個請求會追加到leader的log上,但此時還沒有寫入leader所在節點的文件系統之上
- 這個條leader的log 會在leader發送下一條心跳包的時候攜帶該請求的信息 一起發送給follower
- 當entry提交到follower,且收到多數派的回復之后會給client一個回復,表示集群已經寫入完成。同時將leader的log寫入自己的文件系統,并且發送command讓從節點也進行寫入。這個過程就是multi paxos中的accepted階段。
關于raft更加詳細嚴謹的細節介紹和性能測試 可以參考論文raft paper
強一致算法:ZAB
基本和raft相同,只是在一些名詞的叫法上有一些區別
比如ZAB 將某一個leader的周期稱為epoch,而raft稱為 term。
實現上的話 raft為了保證日志連續性,心跳方向是從leader到follower,ZAB則是相反的。
總結
本篇從理論和實現邏輯上描述了CAP理論、paxos相關的一致性算法變種以及基于multi paxos實現的簡化版強一致算法協議raft和ZAB,后續會從源碼層面一一觀察以及自實現基礎raft的通信來加深對分布式系統設計的理解。
可以看到分布式系統的核心算法還是圍繞CAP理論來進行取舍,從系統前期的角色設計,到每個角色承擔的任務,到最后異常情況下的各個角色做出的反應都能看到CAP的影子。這一些設計原則最終還是會反饋到用戶體驗之上,雙十一的巨量流量,國外的APP群體和國內實時通信交互,看似簡單的一個用戶功能背后的每一個RPC都是一段跨省,跨國的異地戀,即使異國之路被封鎖,合理的系統設計也能夠讓這段戀情持續。
各自安好,靜待他音。
想豬!!!
總結
以上是生活随笔為你收集整理的分布式一致性(共识)算法(Paxos,raft,ZAB)的一些总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喋血复仇怎么刷钱?
- 下一篇: vim 成“神“之路 (一)