從分佈式CAP理論到分佈式一致性協議

前言

在分佈式開發中,我認爲具備CAP理論與瞭解Raft、Zab等分佈式一致性協議是十分有必要的,例如分佈式鎖的選擇,你是選擇Redis的主備集羣(AP模型)還是選擇ZK、etcd(CP模型)呢?關於分佈式鎖的文章 點擊這裏查看 ,如果不具備這些理論知識,我覺得是無法靈活選擇且用好分佈式鎖的,不同業務場景有不同AP、CP模型的需求,例如爲什麼Nacos的配置中心使用CP模型,但註冊中心卻使用AP模型呢?這都是不同的場景有着不同的考量。說了那麼多,那麼爲什麼CAP總是在CP與AP之間討論?接着往下看。

CAP理論

CAP理論是在分佈式集羣環境下討論的,爲什麼分佈式集羣環境下會存在CAP問題呢?舉個例子,假設我們後端存儲服務使用Redis中間件,如果只部署一臺Redis服務器,那麼這臺Redis如果掛了,整個存儲服務都掛了,那麼我們就想要提高它的可用性,怎麼辦呢?最簡單的方法就是加機器

  • 可用性:我部署兩臺Redis機器在一個集羣中(主備集羣),如果此時有一臺Redis掛了我客戶端照樣可以訪問另一臺Redis,保證了一定程度的可用性(保證允許一臺Redis宕機)

同時這兩臺主備Redis需要時刻保持數據同步,這樣在一臺宕機之後另一臺也能保持原有的查詢服務:

  • 一致性:如果我客戶端在A節點的Redis設置了一個M值,然後A節點宕機,此時換B節點的Redis上,如果B節點正好同步了這個M值,就保證了一致性,但是有可能B節點的Redis還沒來得及同步這個M值,我客戶端在B節點讀不到M值,這就存在了一致性問題

那什麼是分區容錯性呢(P)?

  • 分區容錯性:這兩臺主備Redis發生了分區(A、B節點互相連接不上),那就做不了數據同步了,但此時集羣依然向外開放服務,此時集羣具有分區容錯性,如果發生分區,集羣就不能對外服務,則集羣不具有分區容錯性(P特性)

分區容錯性,代表當分佈式節點發生分區(A、B節點互相連接不上)此時的分佈式系統是否還提供服務(是否容錯),如果沒有了P,代表發生分區之後整個分佈式集羣不能使用,這顯然是不行的。下面看看保證P與不保證P的集羣是什麼樣的:

  • 如果沒了P,理論上集羣不容許任何一個節點發生分區,當沒有分區發生時確實可以保證AC(誰能保證集羣系統的節點百分百不會出問題呢?能保證也不需要討論AC問題了),當發生分區時整個集羣不可用,沒有現實意義(如圖1)
  • 如果保證P,說明集羣就只能從A和C選擇其一(如圖2)

綜上所述,所以做一個分佈式系統,P一定需要保證,那麼我們的焦點就在AP與CP的模型去選擇

在這裏插入圖片描述

在這裏插入圖片描述

如果保證P,說明集羣就只能從A和C選擇其一,爲什麼呢?我們重點看圖2中的第四步,在B節點read M值的操作。

  • CP:若此時不讓客戶端讀取B節點的M值,那麼此時不可用,只能讓客戶端讀取A節點,在A節點中M值確實是剛剛客戶端Set M = 1值,M值一致,此時是CP模型,可用性被捨棄。
  • AP:若此時可以讓客戶端讀取B節點的M值,那麼此時節點可用,但是讀取到的值M=0,如果下次又來一個客戶端讀取A節點的M值,卻讀到了M=1,M值不一致,此時是AP模型,一致性被捨棄

分佈式一致性協議

當發生網絡分區或是集羣某個節點掛掉了,我們是否只能從一致性與可用性做抉擇呢?不,作爲成年人,我們兩者都要(狗頭),但兩者我們都各自只拿一半,也就是基本可用與最終一致性(類似BASE理論),這裏我將業界公認的分佈式一致性協議Raft、Zab抽象出來講,看看他們是如果保證CA兩個屬性的。

其實可以Raft、Zab協議可以做到基本可用+強一致性(線性一致性),基本可以算是分佈式一致性算法,非要歸類的話我認爲其屬於CP模型

Raft和Zab在分佈式一致性角度上來說,主要思想是差不多的,所以可以一起抽象來講,一些協議細節建議查看相關論文,這裏只討論如何保證數據一致與基本可用特性

協議的幾個規則

在集羣中節點有Leader、Follower兩種角色,只有Leader節點才負責處理所有的事務操作,Follower節點只需要同步Leader處理的事務操作的數據,有時候也可以提供讀服務。此算法是通過一切以領導者爲準的方式,實現一系列值的共識和各節點日誌的一致。

數據複製

假設此時客戶端請求數據Set M = 0
在這裏插入圖片描述

  • 半數複製原則:只有Leader節點接受事務請求,Leader節點會通知其他節點保存數據,當有半數節點返回OK,Leader節點也保存數據,返回給客戶端保存成功信息(保證數據在超過半數的節點上)

領導者選舉

  • 獲得集羣內大多數節點的選票的節點才能當一個Leader

  • 每一個節點都可以發起選舉讓別人投自己一票,每一個節點在同一個任期號內只能投一票

  • 投給誰?

    • 參照Raft算法,按照先來先服務的標準,誰先來要票,誰就能獲得本節點的選票,但是有一個關鍵是來要票的節點的數據至少不能比本節點舊

      • 數據怎麼算新:任期號最大的最新,任期號一樣的話那麼數據越多的越新,每一屆領導者都有一個epoch任期號的值概念,其爲遞增的值,重新選舉時任期號會進行自增

      先來先服務,若數據都一樣新,且選票被瓜分,選不出領導人怎麼辦?

      • 在Raft算法中使用一個隨機的選舉超時時間,使得每個節點去要票的時間點是隨機的,一羣節點同時交錯去要票轉化爲了一個個節點去要票,Raft在很多地方都做的簡單易懂,還不乏實用性。一個好的算法不僅能work,還能讓人們簡單易懂爲什麼能work,這很關鍵。
      • 在Zab協議中每一個節點都有一個myid,如果節點數據都一樣新,選不出領導人就會比較myid,myid大的即可當選

如何保證一致性

在這裏插入圖片描述
假設此時客戶端在寫入一個值M=1:

根據數據複製規則,Leader只需要保證數據在多數節點上保存下來就可以返回成功

各個節點在各個時期掛了,是否會導致數據的不一致?

  • 若此時Leader在收到半數節點OK之前就掛了,那麼客戶端也會返回一個設置M值錯誤,顯然此時的集羣數據是一致的(從未保存過值)

  • Leader在收到半數節點OK,證明數據在大部分節點上之後,返回給客戶端設置值成功之後Leader節點就掛了,正如圖4那樣,第四步返回客戶端OK信息後宕機,沒來得及複製給S4和S5節點,此時各節點數據如下:

在這裏插入圖片描述
羣龍無首,此時必須選舉出一個新的Leader
在這裏插入圖片描述
可以看出,數據比較舊的S4,S5只能要到兩票,得不到半數的票(半數=(節點/2) + 1,這裏5個節點就是3票),但是數據比較新的S2、S3卻可以得到半數以上的票(任何的節點都會給他們投票,因爲他們的數據至少都不比其他節點舊),根據上面的領導者選舉規則,只有S2、S3有機會能當選Leader,當選後,Leader會通過heartbeat或其他同步數據方式同步Set M = 1 這條日誌,此時集羣數據一致

  • 那既然數據最新的纔可以當選Leader,那如果最新的S2、S3也掛了呢?
    在這裏插入圖片描述
    由圖可以看出,如果最新的S2、S3都掛了,那舊的S4、S5也是隻能拿到兩票(對方和自己總共兩票),此時選不出一個Leader,整個集羣不可用,數據依然可以看作是一致的

    由此可以看出,此分佈式協議容許半數以下的節點宕機,如果半數節點宕機,集羣不可用,所以稱爲基本可用

總結

從上面的討論上來看,半數很關鍵(半數提交、半數選舉),相信看到這裏你會懂得這半數的魔力,接下來總結一下此分佈式一致性協議由於"半數"會有哪些問題:

  • 事務操作的性能瓶頸:由於需要保證半數節點保存了數據,則增刪改數據的性能取決於半數節點的性能(5個節點的寫入性能則取決於3個節點(包括自己)的寫入速度)和與半數節點通信的網絡開銷。這意味着,集羣節點越多(如果想要高可用,那就上多節點),寫入性能有可能會越差(試想,上100個節點,每次寫入都要發給50個節點確保寫入成功,有50次網絡開銷,不過數據複製過程是並行的,木桶效應總耗時取決於半數中最慢的那個節點,不過節點越多,木桶短板出現的概率也就越大,所以這裏說寫入性能是可能變差的)

  • 基本可用:集羣由於需要半數提交才能成功寫入數據,所以如果5個節點宕機2個集羣是可用的,若宕機3個,由於得不到半數的提交,集羣就會不可用,從這點上來看達到了基本可用的特性

  • 最終一致性 or 強一致性(線性一致性):

    如果我們僅僅只是寫入一個值,從寫入角度來說那就是強一致性(因爲都在Leader處理,Raft與Zab都支持順序一致性,即爲你剛剛Set M = 1,又Set M = 2,順序如果反了那結果會變成 M = 1,由於都在同一個Leader節點做,節點維護一個隊列即可保持順序),所以下面更多的是對於讀數據的一致性討論

    • 最終一致性:這取決於具體的實現,若是最終一致性,那麼Follower就開放讀能力,這麼做有個優點,整個集羣的讀能力會隨着節點個數得到提升,但有個缺點,由於半數提交的規則,有可能上一秒你寫入成功了一個值,下一秒在集羣另外某個節點中讀不到這個值(可能還沒同步到),但最終會在一個時間點此值被同步,也就是最終一定會達到一致性
    • 強一致性(線性一致性):如果讀能力只在Leader節點開放,也就是讀我也只能在Leader上讀,那麼此時的一致性是極強的,隨着上一秒的設置值,下一秒一定可以查到剛剛設置的值,缺點就是讀性能得不到擴展,侷限於Leader單點性能的問題(讀寫都在一臺節點上操作)

以上討論了CP模型,對一致性要求比較高的系統比較適用,可以看出來,這種分佈式協議有性能的侷限性,因爲他犧牲了一定的客戶端響應延遲來確保一致性,而且僅僅保證了半數的基本可用性。在延時要求高、高併發場景下這種模型或許並不適用,此時可以考慮AP模型提高響應延遲。

如果是AP模型,相比於一致性協議會簡單一些,因爲他只需要保證可用性,加集羣節點即可,而數據丟失、不一致的情況只會在宕機的那一時刻發生,丟失、不一致的也只是那一時刻的數據,例如Redis的主從集羣架構,主Redis節點只需要異步、定時去同步數據,在寫入時只需要一個節點確認寫入即可返回,延時比一致性協議低,由於Redis在使用上大部分場景都用在緩存,快是他的設計目標,偶爾丟幾條或者不一致幾條緩存數據並不影響場景(關於Redis主從的AP模型的討論更詳細的在這裏)。

回答開篇

爲什麼Nacos配置中心要使用CP模型?

  • 答:CP模型犧牲了一定的延時去換取數據的強一致,但應用的外部化配置並不要求配置成功的延遲要多低,更多的是要保證配置信息的一致性(我配置什麼信息A=1,不要給我弄丟了或者等下查 A 不等於 1,這些都是數據不一致,這肯定是不行的),所以在這種配置場景下是十分適合做CP模型的

爲什麼Nacos註冊中心要使用AP模型?

  • 答:試想一下,微服務的通信,或者說Dubbo的RPC通信,都需要使用到註冊中心去拿到服務的IP地址列表,這關乎業務場景服務調用的延遲問題,延遲這塊就有一定的要求了,那麼再來看看,數據的不一致會導致什麼問題?可能你剛上線了一個A服務實例,此時註冊中心集羣有某個節點掛了,沒註冊上,此實例就不會被服務訪問到(註冊中心沒有這個實例的IP),但也無傷大雅,暫時訪問不到而已,發現問題將服務重啓重新註冊到註冊中心上去即可,再說了,這種情況是剛好註冊實例的時候註冊中心Nacos集羣的Master掛了,纔會導致這種數據不一致的情況,且能接受,這種延遲要求高、數據一致性要求低的場景確實十分適合做AP模型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章