理解分佈式一致性與Raft算法

理解分佈式一致性與Raft算法

永遠繞不開的CAP定理

出於可用性及負載方面考慮,一個分佈式系統中數據必然不會只存在於一臺機器,一致性簡單地說就是分佈式系統中的各個部分保持數據一致html

1-1PF3102KOJ.jpg-18kB

但讓數據保持一致每每並不像看上去那麼簡單,假設咱們有兩臺機器A與B,這時A更新了數據,A須要將更新的指令同步到B,若是A到B網絡傳輸到B數據落地的總時間爲500ms,那麼這個500ms就是可能形成數據不一致的時間窗口,假如兩臺機器分屬不一樣機房,甚至分屬不一樣國家的機房,其時間窗口會更大,具體會形成什麼影響呢?node

舉個栗子🌰,假如用戶a進行轉帳操做,節點A更新了數據,他在轉帳後顯示餘額爲0,但他刷新一下頁面請求到了節點B發現本身的餘額又回到了原來的餘額,然而這只是顯示不一致,但假設他又在節點B上進行在進行了轉帳操做,轉帳中的餘額校驗也依舊訪問的是節點B,那麼可能會形成的影響不言而喻。mysql

CAP定理

在談分佈式一致性以前,咱們首先了解一個定理,那就是CAP定理,請注意,CAP是定理而非理論,CAP定理證實了一個分佈式系統只能同時知足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance)這三項中的兩項。git

truth-of-cap-theorem-diagram1.png-55.2kB

  • 一致性(Consistency):指的就是整個集羣的全部節點數據保持一致
  • 可用性(Availability):在數據同步過程當中,集羣是不是可用狀態
  • 分區容忍性(Partition tolerance):是否可以容忍網絡分區的發生

C和A相對好理解,這裏着重說一下P(Partition tolerance)分區容忍性,聽着比較拗口,說實話,剛開始看到他的時候也是一臉茫然,分區?什麼是分區?其實分區(Partition)簡單的說就是服務器之間的網絡通訊斷了,兩邊的服務器變成兩個獨立的集羣,這就是所謂的分區,斷了的緣由有不少好比交換機故障,路由器故障,掃地阿姨把網線拔了等等,而後什麼是容忍性(tolerance),這個就很好理解了,是否是發生分區了個人服務就再也不提供服務了呢,固然不是,不然也就沒有高可用一說了,那麼咱們可否說不作網絡故障可能發生的假設呢,答案必然是不能的,首先網絡延遲是必然的,網絡延遲的過程當中也能夠將其當作發生分區,另外網絡故障也能夠說是必然的,詳見墨菲定律(滑稽臉)github

其實P也不是徹底不能拋棄的,很簡單,咱們幹掉網線,只保留一臺單機數據庫,就只有一個區,何來分區一說,對啊,因此說咱們常見的傳統單機數據庫(RDBMS)就是能夠知足CA的,如:MySQL,Oracle等等,固然,前提是你沒作主從之類的分佈式方案。因而可知,在全部分佈式系統中P幾乎都是不可拋棄的,因此說咱們的選擇也就只剩兩個了AP和CP。算法

如何理解CP與AP?sql

舉個簡單例子,若咱們集羣有兩臺機器,而兩臺機器網絡發生中斷而致使出現分區:數據庫

  • 若是咱們在雙發沒法通訊的狀況下繼續容許兩邊進行寫入,則必然形成數據的不一致,這時咱們實現了AP而拋棄了C。
  • 但若是咱們禁止其中一方進行寫入,這樣就能夠保證系統的一致性了,但咱們卻由於將一中一個副本置爲不可用而致使了A屬性的喪失,也是說實現了CP。

這樣CAP理論是否變得好理解了一些?固然如今對於CAP理論的爭議也很大,但並非懷疑CAP定理是否能被證僞,而是說CAP理論也許並不適用於咱們一般對數據庫系統的描述,有時咱們並不能簡單的將數據庫劃分爲AP或CP。舉個栗子🌰,若是咱們有一個single master+multiple slaves的mysql數據庫,當leader不可用時,用戶則不能進行寫入,也就喪失了A屬性,但因爲MySQL是經過binlog異步同步數據庫的,用戶也有可能讀到的是舊數據,因此說該系統也許既不知足A也不知足C,僅僅知足了P屬性。安全

(關於CAP的爭議討論推薦閱讀:<a href="https://blog.the-pans.com/cap/">《請不要再稱數據庫是CP或者AP (Please stop calling databases CP or AP)》</a>)服務器

async-replication-diagram.png-80.3kB <br/>

線性一致性與一致性級別

基於CAP理論的AP與CP互斥的原則,針對C的取捨,咱們簡單劃分紅了3個級別來描述(特殊場景下會更多):

強一致性(線性一致性)

強一致性能夠理解爲當寫入操做完成後,任何客戶端去訪問任何存儲節點的值都是最新的值,將分佈式的一致性過程對客戶端透明,客戶端操做一個強一致性的數據庫時感受本身操做的是一個單機數據庫,強一致性就是CAP定理中所描述的C(Consistency),同時下面的講解的Raft算法就是實現線性一致性的一直

弱一致性

弱一致性是與強一致性對立的一種一致性級別,弱一致性簡單地說不去對一致性進行保證,客戶端在寫入成功後依舊可能會獲得舊的值,這也就是捨棄C可能形成的問題,但某些系統下,對一致性的要求並不高,從而能夠捨棄強一致策略可能帶來的性能與可用性消耗。

最終一致性

最終一致性也能夠理解成弱一致性的一種,使用這種一致性級別,依舊可能在寫入後讀到舊值,但作出的改進是要求數據在有限的時間窗口內最終達到一致的狀態。也就說就算如今不一致,也遲早會達到一致,但狹義上的弱一致性並不對一致性作出任何保證,也許某些節點永遠不會達到一致,其實最終一致性的核心就是保證同步的請求不會丟失,在請求到達時節點的狀態變爲最新狀態,而不考慮請求傳輸時的不一致窗口,DNS就是典型的最終一致性系統。 <br/>

Raft算法

Raft算法的論文題目是《In Search of an Understandable Consensus Algorithm (Extended Version)》(《尋找一種易於理解的一致性算法(擴展版)》),很容易理解,Raft算法的初衷就是設計一個相較於Paxos更易於理解的強一致性算法,雖然說更好理解,但依舊畢竟是分佈式一致性算法,其算法複雜程度及各類狀態的多樣性依舊須要較高的理解成本。可是花時間成本去學習Raft是值得的,理解Raft後可以很大程度加深你對分佈式及線性一致性的理解,此次僅是基於我的理解的描述性介紹Raft算法,不對如選舉異常或宕機等狀況的處理作更多探究,若是有什麼疑問歡迎進行討論,同時感興趣的同窗也推薦閱讀Raft原版論文(中文版):<a href="https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md">尋找一種易於理解的一致性算法(擴展版)</a>

Raft算法做爲單純的一致性算法,使用場景並不是僅僅在數據庫,Raft算法將分佈式一致性問題拆分爲若干個子問題進行解決,其餘的相關機制均是這三個子問題的延伸,接下面咱們詳細闡述一下這三個子問題。 注:下面所涉及到全部RPC的協議字段均可以在論文中找到

Leader選舉:避免多節點狀態競爭

一般Raft節點擁有5個節點,將這五個節點分爲三種角色Leader,Follower和Candidate,全部節點只多是這三種角色,而且全部節點的對等的,它們均可以成爲這三種角色的其中之一。 其實雖然有三種角色,但進行抽象之後能夠理解爲集羣只有Leader與Follower兩種角色,Candidate是Leader的預備役而已。 簡單的講能夠將Raft系統理解爲一個一主多從(single master multiple slaves)的RDBMS(MySQL等),但RDBMS一般採用的方案是Master節點用於寫,而Followers用於讀,但Raft不一樣的是無論是讀和寫都要經由Master節點分發給Followers節點,這樣作的缺點是可能會致使Leader節點的負載會高於Followers,但這樣作的好處是實現了強一致性,強一致性的部分下面會詳細說明。 既然是單Leader,那麼假設咱們的Leader宕機了怎麼辦,這時候咱們就能夠將這個問題拆分爲兩個部分:

  1. 如何發現Leader節點宕機
  2. 若是發現Leader節點宕機如何從新選舉Leader

第一個問題在Raft中的解決方案是增長心跳(heartbeat),Leader按期向全部Followers發送心跳消息,若follower在一段時間內沒有收到leader的選票(心跳超時,超時時間隨機,由Follower本身控制),則認爲Leader宕機,開始選舉 第二個問題引伸出了三個概念

  1. 任期號(term)
  2. 選票(vote)
  3. 候選人(candidate)

當Follower認定Leader宕機後,他會挺身而出的認爲本身應該成爲新一輪的Leader,這時他會將本身的狀態轉換爲candidate,並向Node廣播請求選票的RPC,若是超過半數的Node贊成他成爲新的Leader則表明他應得了此次選舉,他就會變成新的Leader。 咱們來簡單說幾個異常的狀況及Raft的處理方式:

  • 雖然心跳超時隨機的策略大幅度減小了兩個Followers同時超時的狀況,但依舊不能保證不會出現兩個candidates同時超時的狀況,假如說出現兩個candidate同時超時的狀況就有可能接連發生兩個candidates同事發起選舉投票,兩個candidates將選票瓜分,最終沒有人可以得到超過半數的選票,這個異常機制的保障措施是candidates當角色發生轉換後candidates會重置超時時間,如若一段時間內沒有新的Leader產生,則從新發起新一輪的選舉,由於全部超時時間均是隨機的,因此第二次發生瓜分選票的可能性已經變得微乎其微。 在論文中也驗證了小幅度的隨機既可讓選票瓜分的狀況大大減少:
只須要在選舉超時時間上使用不多的隨機化就能夠大大避免選票被瓜分的狀況。在沒有隨機化的狀況下,在咱們的測試裏,選舉過程每每都須要花費超過 10 秒鐘因爲太多的選票瓜分的狀況。僅僅增長 5 毫秒的隨機化時間,就大大的改善了選舉過程,如今平均的宕機時間只有 287 毫秒。增長更多的隨機化時間能夠大大改善最壞狀況:經過增長 50 毫秒的隨機化時間,最壞的完成狀況(1000 次嘗試)只要 513 毫秒。
  • 若是Candidate若是接收到其餘Candidate的的選舉請求的話,如何認定到底是繼續收集選票仍是投票給其餘candidate?若是你們一直互相謙讓或者一直互相競爭,豈不是最終誰也不可以成爲Leader了嗎?, 針對這個該問題,在請求選舉的RPC中Raft增長了**任期號(term)**的概念,在raft系統初始化時,全部node的term均爲0,當某一個節點成爲candidate時,該節點就會將term+1併發起選舉,follower僅會投票給RPC中的term大於等於本身的currenttTerm的RPC投票,這樣就避免了無限對等投票的可能性。Raft協議引入了任期號(term)的概念,任期號很簡單,全部node節點在初始化的時候,選票號都爲1,當任何follower當選candidate的時候都會將選票號置加1,並附帶至發起選舉的RPC中,如若其餘節點收到了選舉投票RPC,他會比對自身的選舉號是否比RPC中的選舉號小,若是小於RPC中的選舉號,則他會認可對方的權威 raft-圖4.jpg-46kB

日誌複製:實現指令序列化,是實現強一致性的根本

只有leader節點能夠和客戶端通訊,同時將log複製至全部follower,強制folloer與leader的log保持一致。 所謂日誌,其實就能夠理解爲增刪改查(CRUD)的命令(但其實raft並不關心這個日誌是作什麼的),這個命令在這裏稱爲log,raft將log序列化,log依次複製並執行到每個node,就能實現節點的強一致性,raft中解釋是**「若是有任何的服務器節點已經應用了一個肯定的日誌條目到它的狀態機中,那麼其餘服務器節點不能在同一個日誌索引位置應用一個不一樣的指令」**,也能夠理解爲raft日誌複製的安全性保證是確保全部序列按順序append,不能越過某一個log,這樣就保證了假如查詢發生在修改以後(但可能有不一樣的實現方式),那最終不論訪問的是哪個節點,查詢必然發生在修改以後,這樣就能夠確保拿到的數據是最新的了。 具體實現分爲若干步:

  1. leader接收到客戶端發來的command
  2. leader將當前的index+1,賦給該log,並append到本身的Logs中
  3. leader廣播(RPC)該log給全部follower
  4. 當超過半數的follower迴應接收成功時,leader就將該log commit,並通知全部follower commit raft-圖6.jpg-67.8kB

安全性:保證不一樣的狀態機以相同的順序執行相同的指令

上面提到了Raft算法是如何進行leader選舉和如何進行日誌複製的,至此其實已經能夠實現分佈式一致性了,可是若是想保證日誌提交精準無誤則須要更多地安全性保障措施

1.選舉限制

假如某個candidate在選舉成爲leader時沒有包含全部的已提交日誌,這時就會出現日誌順序不一致的狀況,在其餘一致性算法中會在選舉完成後進行補漏,但這大大增長了複雜性。而Raft則採用了一種簡單的方式避免了這種狀況的發生

  1. Raft在RequestVote RPC(候選人請求成爲Leader的RPC請求) 中增長了本身的日誌信息
  2. 當followers收到RPC請求時則會把candidate的日誌信息與本身的日誌信息進行比較
  3. 假如follower的日誌信息相較於candidate要更新,則拒絕這個選票,反之則贊成該candidate成爲leader

通過這一系列的步驟,則保證了僅容許包含了全部已提交日誌的candidate贏得選舉成爲候選人,從而也就避免了leader缺乏已提交日誌的狀況了

2.延遲提交

上面提到過,咱們進行日誌提交須要三個階段:

  1. leader將log複製到大多數followers
  2. follower將日誌複製,並告訴leader本身已經複製成功
  3. leader收到了大多數followers的複製成功響應,並提交日誌

可是這裏有一個問題,假如leader已經將日誌複製到了大多數followers,但卻在提交以前崩潰了,雖然raft算法中規定後續的leader應該繼續完成以前的複製任務,但在下圖的狀況下依舊會出現已經複製到大多數節點的log依舊被覆蓋掉了。

image.png-182.7kB

在 (a) 中,S1 是領導者,部分的複製了索引位置 2 的日誌條目。 在 (b) 中,S1 崩潰了,而後 S5 在任期 3 裏經過 S三、S4 和本身的選票贏得選舉,而後從客戶端接收了一條不同的日誌條目放在了索引 2 處。 而後到 (c),S5 又崩潰了;S1 從新啓動,選舉成功,開始複製日誌。在這時,來自任期 2 的那條日誌已經被複制到了集羣中的大多數機器上,可是尚未被提交。 若是 S1 在 (d) 中又崩潰了,S5 能夠從新被選舉成功(經過來自 S2,S3 和 S4 的選票),而後覆蓋了他們在索引 2 處的日誌

Raft爲了不這種狀況發生,而規定了一個原則,Raft 永遠不會經過計算副本數目的方式去提交一個以前任期內的日誌條目,也就是說假如這個log的任期已通過了,就算是已經複製到了大多數節點,Raft也不會去提交它,那麼在這種狀況下如何對以前任期的log進行提交呢,這時引入了Raft的Log Matching(日誌匹配原則),該原則的描述是「若是兩個日誌在相同的索引位置上的日誌條目的任期號相同, 那麼咱們就認爲日誌從頭到這個索引位置之間的條目徹底相同」,這個機制的原理是leader在進行日誌複製時會檢查上一條日誌是否一致,若是不一致則會將上一條複製給follower,在複製上一條的過程當中依舊會進行檢查,這樣一個遞歸的過程保證了Raft的Log Matching原則。

原文出處:https://www.cnblogs.com/mokafamily/p/11303534.html

相關文章
相關標籤/搜索