CAP 與 Raft 相關知識

东山絮柳仔發表於2024-07-22

一 . CAP

1.1 CAP的概念

CAP定理指的是在一個分散式系統中,一致性Consistency、可用性Availability、分割槽容錯性Partition tolerance,三者不可兼得。

  • 一致性(C):分散式系統中多個主機之間是否能夠保持資料一致的特性。即,當系統資料發生更新操作後,各個主機中的資料仍然處於一致的狀態。
  • 可用性(A):系統提供的服務必須一直處於可用的狀態,即對於使用者的每一個請求,系統總是可以在有限的時間內對使用者做出響應。
  • 分割槽容錯性(P):分散式系統在遇到任何網路分割槽故障時,仍能夠保證對外提供滿足一致性和可用性的服務。

CAP定理的內容是:對於分散式系統,網路環境相對是不可控的,出現網路分割槽是不可避免的,因此係統必須具備分割槽容錯性。但系統不可能同時保證一致性與可用性。即要麼CP,要麼AP。

1.2 Base 理論

BASE 是 Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent(最終一致性)三個短語的簡寫。Base是對CAP中一致性和可用性權衡的結果,其來源於對大規模網際網路系統分散式實踐的結論,是基於CAP定理逐步演化而來的。

BASE理論的核心思想是:即使無法做到強一致性,但每個系統都可以根據自身的業務特定,採用適當的方式來使系統達到最終一致性。

(1)基本可用

基本可用是指分散式系統在出現不可預知故障的時候,允許損失部分可用性。

(2)軟狀態

軟狀態,是指允許系統資料存在的中間狀態,並認為該中間狀態的存在不會影響系統的整體可用性,即允許系統主機間進行資料同步的過程存在一定延時。軟狀態,其實就是一種灰度狀態,過渡狀態。

(3)最終一致性

最終一致性強調的是系統中所有的資料副本,在經過一段時間的同步後,最總能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終資料能夠達到一致,而不需要保證系統資料的實時一致性。

1.3 一致性描述

(1)實時一致性:要求資料內容一旦發生更新,客戶端立刻可以訪問到最小的資料。所以,在叢集環境下該特性是無法實現的,只存在於單機環境 中。

(2)最終一致性:資料內容發生更新後,經過一小段時間後,客戶端可以訪問到最新的資料。

實時一致性與最終一致性兩個概念是從客戶端訪問到一致性內容的時間角度來說的,但從客戶端訪問到的內容角度來說(不說時間問題),還有下面兩個概念(強/弱一致性)

(3)強一致性:也稱為嚴格一致性。要求客戶端訪問到的一定是更新過的新資料。

(4)弱一致性:允許客戶端從叢集不同節點訪問到的資料是不一致的。

1.4 生產案例

下面將生產中常見的一些中介軟體與伺服器叢集的CAP特性進行分析。

1. Zookeeper 與 CAP

Zookeeper 遵循的是CP模式,即保證了一致性,但犧牲了可用性。

當Leader節點中的資料發生了變化後,在follower還沒有同步完成之前,整個Zookeeper叢集是不對外提供服務的。如果此時有客戶端來訪問資料,則客戶端會因訪問超時而發生重試。不過,由於Leader的選舉非常快,所以,這種重試對於使用者來說幾乎是感知不到的。所以說,Zookeeper保證了一致性,但犧牲了可用性。

2. Consul 與 CAP

Consul 遵循的是CP模式,即保證了一致性,但犧牲了可用性。

3. Redis 與 CAP

Redis 遵循的是AP模式,即保證了可用性,但犧牲了一致性。

4. Eureka 與 CAP

Eureka 遵循的是AP模式,即保證了可用性,但犧牲了一致性。

5.Nacos 與 CAP

Nocos 在做註冊中心時,預設是AP的。但其也支援CP模式,但需要使用者提交請求進行轉換。

二. Raft 演算法

2.1 Raft 演算法概念

Raft 演算法是一種透過對日誌複製管理來達到叢集節點一致性的演算法。這個日誌複製管理發生在叢集節點中的leader 與 follower 之間。 raft透過選舉出的leader節點負責管理日誌複製過程,以實現各個節點之間資料的一致性。

2.2 角色、任期及角色轉變

在raft中,節點有三種角色

Leader:唯一負責處理客戶端寫請求的節點;也可以處理客戶端的讀請求;同時負責日誌複製工作。

Candidate:Leader選舉的候選人,其可能會稱為Leader。是一個選舉中的過程角色。

Follower:可以處理客戶端的讀請求,負責同步來自於Leader的日誌;當接收到其它Candidate的投票請求後可以進行投票;當發現Leader掛了,其會轉變為Candidate發起Leader選舉。

2.3 Leader 選舉

透過raft演算法首先要實現叢集中leader的選舉。

(1)我要選舉

若follower在心跳超時範圍內沒有收到來自leader的心跳,則認為leader掛了。此時其首先會使其本地term增1。然後follower會完成以下步驟:

  • 此時若收到了其它candidate的投票要求,則會將選票投給這個candidate。
  • 如果沒有收到其它candidate的投票要求,則由follower轉變為candidate。
  • 若之前尚未投票,則先投自己一票。
  • 向其它節點發出投票請求,然後等待響應。

注意,每個follower 或者 candidate 只有一票。

(2)我要投票

follower在接收到投票請求後,會根據以下情況來判斷是否投票:

  • 發來投票請求的candidate的term不能小於我的term
  • 在我當前term內,我的選票還沒投出去
  • 若就收到多個candidate的請求,我將採取first-come-first-served方式投票。

(3)等到響應

當一個candidate發出投票請求後,等待其它節點的響應結果。這個響應結果可能有三種情況:

  • 收到過半選票,成為新的leader。然後會將訊息廣播給所有其它節點,以告訴大家我是新的leader了。
  • 接收到別的candidate發來的新leader通知,比較了新leader的term並不比自己的term下,則自己轉變為follower。
  • 經過一段時間後,沒有收到過半選票,也沒有收到新leader通知,則重新發出選舉。

(4)選舉時機

在很大時候,當leader真的掛了,follower幾乎同時會感知到,所以它們幾乎同時會變為candidate發起新的選舉。此時就可能會出現較多candidate票數相同的情況,即無效選舉出leader。

為了防止這種情況的發生,raft演算法其採用了randomized election timeouts 策略來解決這個問題。其會為這些follower隨機分配一個選舉發起時間 election timeout,這個timeout 在150-300ms範圍內。只有到達了election timeout 時間的follower才能轉變為candidate,否則等待。那麼election timeout 較小的follower則會轉變為candidate,然後先發起選舉,一般情況下其會優先獲取到過半選票成為新的leader。

2.4 資料同步

在leader選舉出來的情況下,leader透過日誌複製管理實現叢集中各節點資料的同步。

(1)狀態機

raft 演算法一致性的實現,是基於日誌複製狀態機的。狀態機的最大特徵是,不同server中的狀態機若當前狀態相同,然後接收了相同的輸入,則一定會得到相同的輸出。

(2)處理流程

當leader 接收到client的寫操作請求後,大體會經歷以下流程:

leader 在接收到client的寫操作請求後,就會將資料與自己的term封裝為一個box,請隨著下一次心跳傳送給所有followers,以徵求大家對該box的意見。同時在本地將資料封裝為日誌。

follower在接收到來自leader的box後,首先會比較該box的term與本地的term,只要不比自己的term下,就接收該box,並向leader回覆同意。同時會將該box中的資料封裝為日誌。

當leader接收到過半同意響應後,會將日誌commit到自己的狀態機,狀態機會輸出一個結果,同時日誌狀態變為committed。

同時leader 還會通知所有follower將日誌commit到他們呢本地的狀態機,日誌狀態變為了committed。

在commit通知發出的同時,leader也會向client發出成功處理的響應。

(3)AP支援

Log由 term index、log index 及command 構成。為了保證可用性,各個節點中的日誌可以不完全相同,但leader會不斷給follower傳送box,以使各個節點的log最終達到相同。即raft演算法不是強一致的,而是最終一致的。

2.5 腦裂

raft 叢集存在腦裂問題。在多機房部署中,由於網路連線問題,很容易形成多個分割槽。而多分割槽的形成,很容易產生腦裂,從而導致資料不一致。

下面以三機房部署為例進行分析,根據機房斷網情況,可以分為五種情況;

(1)情況一 :不確定

經過選舉,若新leader產生於B機房,則B與C機房可以正常對外提供服務,此時A機房也存在leader,但不能處理寫操作。故此,出現了腦裂。

若新leader產生於C機房,則A、B、C機房均可正常對外提供服務,不存在腦裂。

(2)情況二:形成腦裂

無論新leader出現的B還是C機房,它們都可對外提供服務。但此時的A機房也存在leader,故此出現了腦裂。A機房只能提供讀操作請求服務。

(3)情況三:無腦裂

A與C機房可以正常對外提供服務,但B機房由於會發起新的leader選舉而全部變為candidate狀態,此狀態時無法對外提供任何服務的。不存在腦裂。

(4)情況四:無腦裂

該場景對A、B、C機房的服務沒有影響。不存在腦裂。

(5)無腦裂

B、C機房不能提供任何服務,因為它們的主機都處於candidate狀態。A 機房只能處理讀操作請求。不存在腦裂。

2.6 Leader 當機處理

(1)請求到達前,leader掛了

client 傳送寫操作請求到達leader之前,leader就掛了,因為請求還沒有到達叢集,所以這個請求對於叢集來說就沒有存在過,對叢集資料的一致性沒有任何影響。leader 掛了之後,會選舉產生新的leader。

由於stale leader【失效的leader;老的leader;原leader】 並未向client 傳送成功處理響應,所以client會重新傳送該寫操作請求。

(2)未開始向follower傳送資料前,leader掛了

client 傳送寫操作請求給leader,請求到達leader後,leader還沒有開始向followers傳送資料,leader就掛了。這時叢集會選舉產生新的leader。stale leader 重啟後會作為follower重新加入叢集,並同步新leader中的資料以保證資料一致性。之前接收到client的資料被丟棄。

由於stale leader 並未向client 傳送成功處理響應,所以client會重新傳送該寫操作請求。

(3)傳送了部分後,leader掛了

client 傳送寫操作請求給leader,leader接收完資料後,向所有的followers傳送資料。在部分follower接收到資料後,leader掛了。由於leader掛了,就會發起新的leader請求。

若leader產生於已完成資料接收的follower,其會繼續將前面接收到的寫操作請求轉換為日誌,並寫入到本地狀態機,並向所有follower發出詢問。在獲取過半同意響應後,會向所有followers發出commit指令。同時向client進行相應。

若leader產生於尚未完成資料接收的follower,那麼原來已完成資料接收的follower則會放棄曾經接收到的資料。由於client沒有接收到相應,所以client會重新傳送該寫操作請求。

(4)commit 通知發出後,leader掛了

client 傳送寫操作請求給leader,leader也成功向所有followers 發出commit指令,並向client 發出相應後,leader掛了。

由於stale leader 已經向client傳送成功接收響應,且commit 通知已經發出,說明這個寫操作請求已經被server成功處理。

2.7 Raft 演算法動畫演示

此網址透過動畫演示了Raft演算法的工作原理,網址為:http://thesecretlivesofdata.com/raft/

這個網址開啟可能有點慢,要有耐心。

學習參閱特別宣告

【Redis影片從入門到高階】

【https://www.bilibili.com/video/BV1U24y1y7jF?p=11&vd_source=0e347fbc6c2b049143afaa5a15abfc1c】