分佈式系統一致性保障

算法 通信 工作這一年 架構思維 2019-05-08

本文梳理從單點到分佈式遇到的概念及問題,包括:

  • 單點問題
  • 主從
  • 集群
  • 負載均衡
  • 分佈式
  • 分佈式理論:CAP,BASE
  • 一致性:2PC,3PC,TCC,消息事務,Paxos,Raft

架構風格:萬金油CS與分層中提到了CS架構風格。可以說CS架構風格是分佈式架構的起點,所以我們從CS架構風格開始。

最簡單的CS架構就是一個Client+一個Server!Client和Server之間建立連接,然後Server對Client的請求進行響應。

對於這個最簡單的CS架構,有一個很明顯的問題:「單點問題

即當這唯一的一個Server掛掉以後,Client就無法獲得響應了。那怎麼解決單點問題呢?非常簡單,冗餘

冗餘的方法有兩種:主從和集群

注意:分佈式並不是為了解決單點問題的,而是為了解決單臺服務器無法支撐整體業務的問題

主從、集群和分佈式

下面舉個例子來說明,主從、集群和分佈式的區別!

假設我老婆開了家飯店。因為沒錢,早期客戶也不是很多,所以就一個人張羅。這就是一個單點。因為如果我老婆有什麼事情,那就沒法開店了。

分佈式系統一致性保障

可是我家有兩個小孩,老是生病,三天兩頭的要跑醫院,老是關門也不是辦法。所以,在老婆有事的時候,我就去開店。我和我老婆就是「主備關係」。

分佈式系統一致性保障

但是呢,我也要上班,我不可能老是去開店。所以我們都沒空的時候,就讓我丈母孃幫忙開店。現在我、我老婆和丈母孃之間的關係還是「主備關係」!為什麼?因為我們沒有同時開店,只是在其他人都有事的時候才來開店的。也就是說同時只有一個服務對外提供服務。

一段時間後,飯店生意很好。老婆一個人有點忙不過來了。於是我就乾脆辭職和老婆一起開店。我們做的事情相同。於是我們就組成了「集群」。我們同時對外提供相同的服務。

分佈式系統一致性保障

此時如果我老婆有事,那我又變成了一個單點服務。

再後來,我們兩個人也忙不過來了。我們就又招了個服務員。服務員主要工作就是幫忙點菜、收拾碗筷之類的。我們就專心的做飯、收錢。現在我和我老婆之間還是集群,因為我們的工作一樣。而我、我老婆和服務員就組成了分佈式。我們三個人組成了完整的服務。

分佈式系統一致性保障

但是呢,現在服務員出現了單點問題。當這個服務員請假的話,我們又忙不過來了。於是我們又招了一個服務員。這兩個服務員之間又組成了集群。

分佈式系統一致性保障

最後,飯店越開越大。我們招了服務員、收銀、廚師、洗碗工、清潔員.....組成了一個大的分佈式系統。

分佈式系統一致性保障

而我和我老婆則出去浪去了。

分佈式系統一致性保障

三者的區別

從上面的例子,我們可以看出,主從、集群和分佈式之間的區別:

  • 主從:同時只有一臺服務對外提供服務。當這臺服務器掛掉以後,另外一個服務器對外提供服務。
  • 集群:所有服務器同時對外提供服務。有一臺或幾臺服務器掛掉以後,可能有部分客戶端會受到影響(看負載均衡算法)
  • 分佈式:所有的服務組合成一個完成的系統對外提供服務。如果有一個或多個服務掛掉,那對應的那部分功能就沒法使用。每個服務都可以通過集群來確保可用性。

新的問題

雖然主從和集群解決了單點的問題。但是從主從到集群、再到分佈式,系統間通信成指數級增長,也引入了各種其它問題。

在單點CS架構下,只有Client和Server之間存在網絡通信,Server內部並不存在網絡通信。而變成分佈式以後,Server間也需要通過網絡通信。由於網絡的不穩定,可能會出現各種問題。同時由於節點數的增加,Server本身出問題的機率也就提高了,主要可能出現:

  • 通信異常:由於網絡的不穩定性,可能導致服務間的通信出現異常
  • 網絡分區:由於「通信異常」,可能導致服務間的數據不同步,或者部分服務無法對外服務。就是說,可能客戶端訪問到的響應有差異。
  • 響應超時:一般來說,正常的調用會返回成功或失敗的狀態,但是網絡通信可能出現超時。超時分為
  • 發送超時:請求並沒有到達服務端
  • 響應超時:請求到達了服務端,但是服務端的響應並沒有返回到客戶端
  • 處理方式:如果請求是冪等的,那可以再次請求。如果不是冪等的,那需要一些補償措施。
  • 節點故障:服務節點宕機,無法對外提供服務。
  • 節點選擇:即Client到哪個節點去獲取數據。這就涉及到負載均衡算法,一般的負載均衡算法有(這裡不展開)
  • 隨機
  • 輪詢
  • 加權隨機
  • 加權輪詢
  • 源地址Hash
  • 一致性Hash

實際上在分佈式系統中,要解決的問題是:「在網絡不可靠的情況下,如何保證數據一致性」?

處理方式有兩種:

  • 想辦法保證強一致性
  • 不保證強一致性,但保證最終一致性

強一致性方案

保證強一致性的思路其實很簡單,就是「假設網絡在大部分情況下都是正常的,批量的去操作這一批節點,要麼全部成功,要麼全部失敗」!

操作方式有:

  • 2PC(兩階段提交)
  • 事務補償TCC(可以說是業務層面的2PC)
  • 3PC(三階段提交)
  • Sagas:將分佈式長事務拆分成多個短事務,由Sagas引擎來協調,具體沒有研究。可參考最後的參考資料。

2PC和3PC都引入了一個「協調者」來協調各個節點(參與者):

  • 客戶端提交操作到協調者
  • 協調者與參與者通信,確保各個參與者的操作
  • 如果所有參與者操作成功,協調者返回客戶端成功
  • 否則協調者返回客戶端失敗

具體流程見下文。

2PC

流程

  • 提交請求階段(commit-request phase)
  • 協調者向所有參與者詢問是否可以執行提交操作,然後開始等待各參與者的響應
  • 參與者直接就開始執行事務操作,並將Undo信息和Redo信息寫入日誌
  • 如果參與者的事務操作執行成功,則返回一個「同意」消息;否則返回一個「終止」消息
  • 提交階段(commit phase)
  • 當協調者從所有參與者獲得的響應消息都為「同意」:
  • 協調者向所有參與者發出「正式提交」的請求
  • 參與者提交事務,並釋放事務佔用的資源
  • 參與者向協調者發送「完成」消息
  • 協調者收到所有參與者反饋的「完成」消息後,完成事務
  • 如果任一參與者在第一階段返回「終止」消息,或者協調者在第一階段超時之前無法獲取所有參與者的響應消息:
  • 協調者向所有參與者發出「回滾操作」的請求
  • 參與者利用之前寫入的Undo信息執行回滾操作,並釋放在整個事務期間佔用的資源
  • 參與者向協調者發送「回滾完成」消息
  • 協調者收到所有參與者反饋的「回滾完成」消息後,取消事務

舉例

假設兩個Server(s1,s2)向三個註冊中心節點註冊(c1,c2,c3),如果是2PC,流程如下:

  • s1向c1,c2,c3發送「提交請求」,c1,c2,c3接收到了消息,將s1信息保存,並寫入Undo和Redo日誌,返回「成功」
  • 此時s2向c1,c2,c3發送「提交請求」,但是c1,c2,c3目前被鎖定了,所以只能等待
  • s1收到所有的「成功」消息,向c1,c2,c3發送「確認提交」請求,c1,c2,c3提交事務,返回「完成」,s1事務結束
  • s2超時沒等到所有的「成功」消息,事務回滾。

問題

二階段提交的問題很明顯:

  • 節點阻塞:在請求階段,節點就執行了事務操作,並進入阻塞狀態。這會導致幾個連鎖問題:
  • 節點本身阻塞,在這個事務結束之前,這個節點不響應其它請求(上面的例子就是這種情況)
  • 如果節點佔用了公共資源,其它想要訪問公共資源的第三方節點也會進入阻塞狀態
  • 如果在第二階段出現網絡故障,則節點會一直阻塞(可使用超時機制)
  • 數據不一致:在第二階段提交事務時,可能網絡故障了,只有部分節點提交了數據。這就導致了數據不一致。
  • 超時失敗:第二階段,參與者執行完成了,並全部返回完成,但是協調者沒有接收到,導致執行了回滾操作
  • 所有參與者都回滾了,事務執行失敗(實際事務都執行成功了,但是沒有接收到響應)
  • 部分參與者回滾,又導致了數據不一致

所以2PC並不能保證真正的一致性。

XA是基於2PC制定的分佈式事務規範。JTA是基於XA實現的Java事務接口。

事務補償TCC

TCC將整個業務邏輯分為三塊:Try、Confirm和Cancel三個操作:

  • Try:對業務系統做檢測及資源預留。類似2PC的階段一
  • Confirm:對業務系統做確認提交。默認只要Try成功,Confirm一定成功。
  • Cancel:在業務執行錯誤時,執行的業務取消,釋放預留資源

舉例

還是假設兩個Server(s1,s2)向三個註冊中心節點註冊(c1,c2,c3),如果是TCC,流程如下:

  • s1向c1,c2,c3發送「Try請求」,c1,c2,c3接收到了消息,鎖定服務列表信息,返回「成功」
  • 此時s2向c1,c2,c3發送「Try請求」,鎖定失敗,可以等待或重試
  • s1收到所有的「成功」消息,向c1,c2,c3發送「Confirm提交」請求,c1,c2,c3提交事務,返回「完成」,s1事務結束

問題

TCC的主要問題是適用性問題,因為是業務層的事務管理,所以需要針對具體的業務進行具體的Try/Confirm/Cancel的實現。

3PC

為了解決2PC的阻塞問題,就有了三階段提交。三階段提交將二階段提交的「請求階段」拆分為「CanCommit階段」和「PreCommit階段」。具體流程如下:

  • CanCommit階段: 協調者向參與者發送提交請求,參與者如果可以提交就返回「是」,否則返回「否」。(注意,這裡並不會執行事務,所以也不會阻塞)
  • PreCommit階段
  • 如果參與者都返回「是」,則進行事務的預執行:
  • 協調者向參與者發送PreCommit請求,並進入Prepared階段
  • 參與者接收到PreCommit請求後,執行事務操作,並將undo和redo信息記錄到事務日誌中
  • 如果參與者執行成功,則返回「完成」消息,並等待最終指令
  • 假如有任何一個參與者向協調者發送了「否」,或者等待超時,那麼就中斷事務:
  • 協調者向所有參與者發送終止請求
  • 參與者收到終止請求後(或超時仍未收到協調者的請求),則終止事務
  • DoCommit階段:該階段提交事務,也分為兩種情況:
  • 如果協調者接收到所有參與者返回的「完成」,則從PreCommit狀態進入DoCommit狀態,同時向所有參與者發送doCommit請求
  • 參與者接收到doCommit請求之後,提交事務,並在完成之後釋放所有的資源
  • 事務提交完之後,向協調者發送「提交完成」響應
  • 協調者接收到所有參與者的「提交完成」響應之後,完成事務
  • 如果協調者沒有接收到所有參與者發送的「提交完成」響應(參與者返回的不是「提交完成」響應或者超時沒有返回任何信息),則終止事務

舉例

依然假設兩個Server(s1,s2)向三個註冊中心節點註冊(c1,c2,c3),如果是3PC,流程如下:

  • s1向c1,c2,c3發送「CanCommit請求」,c1,c2,c3接收到了消息,返回「OK」
  • 此時s2向c1,c2,c3發送「CanCommit請求」,c1,c2,c3接收到了消息,也返回「OK」
  • s1收到所有的「成功」消息,向c1,c2,c3發送「PreCommit請求」,c1,c2,c3將s1信息保存,並寫入Undo和Redo日誌,返回「成功」
  • s2收到所有的「成功」消息,向c1,c2,c3發送「PreCommit請求」,c1,c2,c3返回「失敗」
  • s1收到所有的「成功」消息,向c1,c2,c3發送「DoCommit請求」,c1,c2,c3提交事務,釋放資源,返回成功。事務完成
  • s2收到「失敗」消息,事務回滾

問題

3PC同樣會出現數據不一致的情況,在第三階段協調者終止事務,但是參與者沒有接收到,則可能導致數據不一致。

最終一致性

可以看到,強一致性在任何一個節點出現問題後,事務都會失敗。在事務要求不是很高的情況下,並不一定要保證強一致性,只要保證最終一致性就可以了。保證最終一致性的思路是基於CAP定理和BASE理論。

CAP定理

《JDBC的架構設計》中提到了本地事務的ACID特性:

  • 原子性(Atomicity):事務作為一個整體被執行,包含在其中的對數據庫的操作要麼全部被執行,要麼都不執行
  • 一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是數據庫中的數據應滿足完整性約束
  • 隔離性(Isolation):多個事務併發執行時,一個事務的執行不應影響其他事務的執行
  • 持久性(Durability):已被提交的事務對數據庫的修改應該永久保存在數據庫中

CAP裡也有一個A和一個C。但是實際兩者之間並沒有什麼關係。CAP中的C、A、P分別是:

  • 一致性(Consistency):這裡的一致性指的是數據在多個副本之間保持強一致
  • 可用性(Availability):指系統可以對外提供服務,這是一個架構屬性。關於架構屬性可參考之前的文章《什麼是架構屬性》
  • 分區容錯性(Partition tolerance):這裡的分區指的是「網絡分區」。指的是除非整個網絡環境都發生了故障,否則系統在遇到任何網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務

CAP定理,說的是「對於一個分佈式系統來說,不可能同時滿足一致性、可用性和分區容錯性。最多隻能滿足其中兩項」!

那我們應該滿足哪兩項呢?這個都知道了,就是「分區容錯性」和「可用性」,同時保證「最終一致性」!那為什麼呢?

我們來假設:

  • 假如放棄可用性:即系統遇到任何問題,就對外不可用。上面說了,分佈式系統網絡間通信數量大大增加,如果網絡一波動,系統就不可用。那實際可用性還不如單點應用。那還要搞分佈式幹嘛?
  • 假如放棄分區容錯性:避免網絡分區的方法就是將數據集中管理,又變成了單點應用了。
  • 假如放棄強一致性:強一致性指的是像數據庫事務那樣,操作完了以後,所有的數據都是一致的。但是在分佈式系統裡面,如果出現了網絡分區或者網絡延時,那麼是無法保證強一致性的。雖然放棄了強一致性,但是可以使用各種手段來保證最終一致性。就是說當網絡故障恢復後,或者網絡通信結束後,保證各個副本的數據一致。

BASE理論

BASE理論是CAP的實踐理論。其核心思想是:「根據CAP定理,我們知道系統是無法做到強一致性的,但系統可以根據自身的業務特點,採用適當的方式來使系統達到最終一致。」

BASE包括:

  • 基本可用(Basically Available):在系統出現故障時,相對於正常的系統來說,可能有某些服務降級,但是還是能正常對外提供服務。服務降級包括:
  • 響應時間:即比正常系統響應時間慢
  • 功能:某些功能可能不可用。比如18年雙11,淘寶的地址服務就掛了,但是不影響用戶敗家,只是不能修改地址而已。
  • 軟狀態(Soft State):指系統在最終一致之前的中間狀態(即數據在一部分節點中,但還沒有同步到所有的節點),該狀態不影響系統的整體可用性。
  • 最終一致性(Eventually Consistent):系統從軟狀態中,經過一段時間,最終需要達到數據一致。這個「時間」取決於網絡延時、系統負載、數據複製方案等因素。

可以看出,現在的主要問題就是「如何保證最終一致性」?

最終一致性方案

保證最終一致性的方法有:

  • 消息事務
  • Paxos算法
  • Raft

消息事務

消息事務的原理很簡單,通過消息隊列來保障最終一致性,大致流程為:

  • 在提交事務時,需要提交一份數據到消息隊列。兩者要麼全部成功,要麼全部失敗
  • 消息隊列消費端消費消息,執行後續的任務
  • 如果執行成功則結束
  • 如果因為網絡原因執行失敗,則重試。如果因為業務原因執行失敗,則可以寫入一個補償消息。消息生產方進行數據回滾

舉例

還是假設Server(s1,s2)向三個註冊中心節點註冊(c1,c2,c3),消息事務流程如下:

  • s1向c1和消息隊列提交註冊信息,提交成功
  • s2向c1和消息隊列提交註冊信息,提交成功
  • c2,c3監聽到消息隊列的消息,依次執行註冊信息
  • 執行成功則c1,c2,c3的服務列表最終都是一致的
  • 如果c2執行s2失敗,則需要向消息隊列發送一個撤銷操作,c1,c2接收到消息後,撤銷s2的信息,s2接收到消息後,事務失敗。

Paxos算法

普遍認為Paxos比較難理解,即使Lamport不使用任何公式寫的《Paxos Made Simple》論文也都比較難以理解。

我覺得《Paxos Made Simple》難以理解的原因有兩個:

  • 論文中沒有明確「提案」和「Value」之間的關係,前面說「Value」,後面又說「提案」。實際上「提案」包括了「提案編號」和「提案值」(也就是Value),提案編號是一個全序ID,即全局唯一且是遞增的
  • 論文裡面描述的Paxos算法分為兩個階段,但是這裡的兩個階段和2PC裡所說的兩階段意義是不同的。2PC中的兩個階段是它的完整流程就是兩個階段,而Paxos裡的兩個階段是交替執行的,直到達到最終一致。也就是說,對一次Paxos計算來說,可能要經過多次的階段一、階段二,才會達到最終一致

論文中對Paxos算法的流程描述如下:

階段一

  • Proposer選擇一個提案號n,向大部分的Acceptor發送攜帶了提案號n的prepare請求
  • 如果Acceptor接收到了prepare請求,請求攜帶的提案號n大於它所響應的prepare請求所攜帶的提案號,那麼它會返回一個響應,這個響應包含了它所通過的最大編號的提案(如果存在的話)
  • ,並保證以後都不會再接收提案號小於n的請求了
  • 階段二
  • 如果Proposer接收到了大部分Acceptor的響應,然後Proposer就發送accept請求給Acceptor,這個accept請求包含了提案號n和值v,這個v是前面Acceptor返回的最大編號的提案裡的值,如果Acceptor沒有返回任何提案,則v由Proposer自定義
  • 如果Acceptor接收到了提案號為n的accept請求,如果它沒有響應任何提案號大於n的prepare請求,它就接收這個提案
Paxos中有三個角色Proposer提出提案,Acceptor接收提案,Learner獲取提案

它主要保證了,在一次提案提交過程中(也就是一個Paxos計算中):

  • 只有提案被提交了,那麼它裡面的值才能被選中
  • 最終只能有一個值被選中
  • 如果某個進程獲取到了某個值,那麼這個值一定是被選中的那個值

舉例

下面我還是以註冊中心的流程,來闡述BasicPaxos算法(MultiPaxos這裡不討論)!

在這裡Server可以說是Proposer,註冊中心則是Acceptor,Client則是Learner。

假設有三個Server,P1[192.168.1.1],P2[192.168.1.2],P3[192.168.1.3]。五個註冊中心節點A1,A2,A3,A4,A5。三個客戶端L1,L2,L3。現在P1,P2都是第一次註冊到註冊中心(第一次計算):

  • P1,P2從全局ID生成器那裡分別獲取一個提案編號,P1[100],P2[101]
  • P1攜帶提案編號100向A1,A3,A5發送prepare請求。但是因為網絡不太好,A1,A3成功接收,但是A5發送失敗了。A1,A3接收到了提案,記錄下提案編號100,並保證以後不會再同意編號小於100的提案。因為是第一次接收提案,所以A1,A3都沒有值返回。
  • P2攜帶提案編號101向A1,A2,A5發送prepare請求。都發送成功了。A2,A5是第一次接收到提案,所以直接記錄下提案編號101,並保證以後不會再同意編號小於101的提案。因為是第一次接收提案,所以A2,A5都沒有值返回。對於A1來說,因為已經接收到P1的提案,但是101大於100,所以記錄下101,並保證以後不會再同意編號小於101的提案。雖然A1接收了P1的prepare請求,但是並沒有接收到任何值,所以也沒有返回。
  • P1接收到了A1,A3的響應,但是沒有滿足大多數。所以它重新獲取了一個提案編號120,準備重新發送。
  • 在此之前,P2接收到了A1,A2,A5的響應,將自己的IP作為值,並攜帶編號101一起[101,[192.168.1.2]],向A1,A2,A5發送accept請求。A1,A2,A5都接收了這個提案,並記錄了下來。
  • P1重新向A1,A3,A5發送prepare請求[120]。因為120大於A1,A3,A5所記錄的提案號,所以A1,A3,A5都記錄下來這個提案編號。由於A1,A5已經記錄了P2的值,所以返回P2的值給P1。A3沒有返回。
  • P1接收到了A1,A3,A5的響應,開始發送accept請求,但是A1,A5返回了P2的值,所以P1只能發送[120,[192.168.1.2]]到A1,A3,A5。
  • 第一次計算結束,目前A1,A3,A5記錄的是[120,[192.168.1.2]]。A2記錄的是[101,[192.168.1.2]],雖然編號不一樣,但是值是一樣的。A4則可以通過學習得到最終的服務列表。

但是P1明顯沒有註冊上去,所以它又開始了第二次註冊嘗試(第二次Paxos計算),這時呢,P3也開始註冊了:

  • P1,P3從全局ID生成器那裡分別獲取一個提案編號,P1[210],P3[211]
  • P1攜帶提案編號210向A1,A3,A5發送prepare請求。A1,A3,A5接收到了提案,記錄下提案編號210,並保證以後不會再同意編號小於210的提案。因為這裡是新的一次Paxos計算,所以這裡實際還是第一次接收數據,所以不會返回提案值。
  • P3攜帶提案編號211向A2,A4,A5發送prepare請求。因為211大於210,所以A2,A4,A5接收到了提案,記錄下提案編號211,並保證以後不會再同意編號小於211的提案。這裡也不會返回提案值。
  • P1接收到了響應,開始發送access請求[210,[192.168.1.1]]到A1,A3,A5。A1,A3接收了這個提案,但是A5的提案號現在是211,它不接收這個提案。
  • P3接收到了響應,開始發送access請求[211,[192.168.1.3]]到A2,A4,A5。A2,A4,A5接收了這個提案。
  • P1重新獲取了新的ID220,再次向A1,A3,A5發送prepare請求,A1,A3,A5都同意了。A1,A3返回[210,[192.168.1.1]],A5返回[211,[192.168.1.3]]
  • P1接收到響應,只能發送[220,[192.168.1.3]]的prepare請求到A1,A3,A5。A1,A3,A5全部同意。
  • 至此第二次計算結束,目前A1,A3,A5記錄的是[220,[192.168.1.3]]。P3,A2,A4記錄的是[211,[192.168.1.3]],雖然編號不一樣,但是值是一樣的。P2則通過學習可以得到最終的服務列表。

現在P1還是沒註冊上去,所以再來第三次註冊嘗試(第二次Paxos計算),現在就它一個註冊了:

  • P1從全局ID生成器那裡獲取一個提案編號P1[310]
  • P1攜帶提案編號310向A1,A3,A5發送prepare請求。A1,A3,A5接收到了提案,記錄下提案編號310,並保證以後不會再同意編號小於310的提案。因為這裡是新的一次Paxos計算,所以這裡實際還是第一次接收數據,所以不會返回提案值。
  • P1接收到了響應,開始發送access請求[310,[192.168.1.1]]到A1,A3,A5。A1,A3,A5接收了這個提案。
  • 其它節點可以通過學習,獲得最終的服務列表。

Raft

Raft中也有三個角色:

  • Leader(領袖):處理客戶端交互,一般一次只有一個Leader(如果出現網絡分區,可能會出現多個Leader,但不影響最終一致性)
  • Follower(群眾):默認情況下都是Follower,Leader從Follower中選舉出來
  • Candidate(候選人):將要被選為Leader的Follower

Raft也分為兩個階段:

  • 選舉階段
  • 由選舉出來的Learder負責和客戶端交互,同步各個Follower

整個過程和美國大選類似:

  • 選舉階段(選總統)
  • Follower在隨機等待一段時間後,轉換為Candidate,立即先給自己投一票,然後向其它的Follower拉票,Follower進行投票,投票多的那個就是Leader
  • 如果出現了兩個Candidate票數相同的,那麼就再等待一段時間,由這兩個Candidate再次拉票,票數多的就是Leader
  • 選出來的Leader通過心跳機制和Follower確立leader地位。如果Leader掛了,則重新選出新的Leader(這個Leader的數據必須是最新的)
  • Leader階段(總統處理國事,並公告)
  • Leader接收到Client的消息後,此時為Uncommitted狀態
  • Leader向所有的Follower複製這個消息,並等待Follower的響應
  • 當大多數的Follower返回後,Leader返回Client成功響應。此時為Committed狀態
  • Leader告知Follower,該數據已提交

舉例

還是假設Server(s1,s2)向三個註冊中心節點註冊(c1,c2,c3),Raft流程如下:

  • c1,c2,c3先選舉出一個Leader,假設c1勝出
  • c1和c2,c3建立心跳,並等待Server註冊
  • s1發送註冊信息,c1接收,然後複製給c2和c3
  • s2發送註冊信息,c1接收,然後複製給c2和c3。這裡相當於已經退化成了單CS架構了
  • c1接收到c2,c3成功接收s1消息的響應,告知s1,事務成功
  • 但是c2接收s2消息失敗,此時可以嘗試重試,或者回滾事務

一致性方案選擇

「強一致性」較「最終一致性」可靠性和性能都較差(Paxos由於邏輯複雜性能也不行),但是實現簡單,「最終一致性」則反之。方案的選擇,視具體情況而定:

  • 對於需要強一致性的業務來說,則放棄部分性能,使用強一致性。比如轉賬業務
  • 對於性能要求高,但是數據一致性要求並不是太強的業務,可以使用最終一致性。比如大V發微博

參考資料

  • 《PaxosMadeSimple》http://lamport.azurewebsites.net/pubs/pubs.html#paxos-simple
  • 《Base: An Acid Alternative》https://queue.acm.org/detail.cfm?id=1394128
  • Wiki二階段提交https://zh.wikipedia.org/wiki/%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4
  • Wiki三階段提交https://zh.wikipedia.org/wiki/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4
  • Wiki XAhttps://zh.wikipedia.org/wiki/X/Open_XA
  • Sagashttps://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
  • CONSENSUS: BRIDGING THEORY AND PRACTICEhttps://ramcloud.stanford.edu/~ongaro/thesis.pdf

相關推薦

推薦中...