分佈式存儲之CAP理論

什麼是 CAP?

我們結合電商的例子,來理解 CAP 的含義。假設某電商,在北京、杭州、上海三個城市建立了倉庫,同時建立了對應的服務器{A, B, C}用於存儲商品信息。比如,某電吹風在北京倉庫有 20 個,在杭州倉庫有 10 個,在上海倉庫有 30 個。那麼,CAP 這三個字母在這個例子中分別代表什麼呢?

  • C 代表 Consistency,一致性,是指所有節點在同一時刻的數據是相同的,即更新操作執行結束並響應用戶完成後,所有節點存儲的數據會保持相同。在電商系統中,A、B、C 中存儲的該電吹風的數量應該是 20+10+30=60。假設,現在有一個北京用戶買走一個電吹風,服務器 A 會更新數據爲 60-1=59,與此同時要求 B 和 C 也更新爲 59,以保證在同一時刻,無論訪問 A、B、C 中的哪個服務器,得到的數據均是 59。
  • A 代表 Availability,可用性,是指系統提供的服務一直處於可用狀態,對於用戶的請求可即時響應。在電商系統中,用戶在任一時刻向 A、B、C 中的任一服務器發出請求時,均可得到即時響應,比如查詢商品信息等。
  • P 代表 Partition Tolerance,分區容錯性,是指在分佈式系統遇到網絡分區的情況下,仍然可以響應用戶的請求。網絡分區是指因爲網絡故障導致網絡不連通,不同節點分佈在不同的子網絡中,各個子網絡內網絡正常。在電商系統中,假設 C 與 A 和 B 的網絡都不通了,A 和 B 是相通的。也就是說,形成了兩個分區{A, B}和{C},在這種情況下,系統仍能響應用戶請求。

**一致性、可用性和分區容錯性,就是分佈式系統的三個特徵。**那麼,我們平時說的 CAP 理論又是什麼呢?CAP 理論指的就是,在分佈式系統中 C、A、P 這三個特徵不能同時滿足,只能滿足其中兩個,如下圖所示。這,是不是有點像分佈式系統在說,這頂“帽子”我不想要呢?
在這裏插入圖片描述
接下來,我就通過一個例子和你進一步解釋下,什麼是 CAP 以及 CAP 爲什麼不能同時滿足吧。

如下圖所示,網絡中有兩臺服務器 Server1Server2,分別部署了數據庫 DB1 和 DB2,這兩臺機器組成一個服務集羣,DB1 和 DB2 兩個數據庫中的數據要保持一致,共同爲用戶提供服務。用戶 User1 可以向 Server1 發起查詢數據的請求,用戶 User2 可以向服務器 Server2 發起查詢數據的請求,它們共同組成了一個分佈式系統。
在這裏插入圖片描述
對這個系統來說,分別滿足 C、A 和 P 指的是:

  • 在滿足一致性 C 的情況下,Server1 和 Server2 中的數據庫始終保持一致,即 DB1 和 DB2內容要始終保持相同;
  • 在滿足可用性 A 的情況下,用戶無論訪問 Server1 還是 Server2,都會得到即時響應;
  • 在滿足分區容錯性 P的情況下,Server1 和 Server2 之間即使出現網絡故障也不會影響 Server1 和 Server2 分別處理用戶的請求。

當用戶發起請求時,收到請求的服務器會及時響應,並將用戶更新的數據同步到另一臺服務器,保證數據一致性。具體的工作流程,如下所示:

  • 用戶 User1 向服務器 Server1 發起請求,將數據庫 DB1 中的數據 a 由 1 改爲 2;
  • 系統會進行數據同步,即圖中的 S 操作,將 Server1 中 DB1 的修改同步到服務器 Server2 中,使得 DB2 中的數據 a 也被修改爲 2;
  • 當 User2 向 Server2 發起讀取數據 a 的請求時,會得到 a 最新的數據值 2。
    在這裏插入圖片描述

這其實是在網絡環境穩定、系統無故障的情況下的工作流程。但在實際場景中,網絡環境不可能百分之百不出故障,比如網絡擁塞、網卡故障等,會導致網絡故障或不通,從而導致節點之間無法通信,或者集羣中節點被劃分爲多個分區,分區中的節點之間可通信,分區間不可通信。
這種由網絡故障導致的集羣分區情況,通常被稱爲“網絡分區”。在分佈式系統中,網絡分區不可避免,因此分區容錯性 P 必須滿足

接下來,我們就來討論一下在滿足分區容錯性 P 的情況下,一致性 C 和可用性 A 是否可以同時滿足。

  • 假設,Server1 和 Server2 之間網絡出現故障,User1 向 Server1 發送請求,將數據庫 DB1 中的數據 a 由 1 修改爲 2,而 Server2 由於與 Server1 無法連接導致數據無法同步,所以 DB2 中 a 依舊是 1。這時,User2 向 Server2 發送讀取數據 a 的請求時,Server2 無法給用戶返回最新數據,那麼該如何處理呢?
  • 我們能想到的處理方式有如下兩種:
    第一種處理方式是,保證一致性 C,犧牲可用性 A:Server2 選擇讓 User2 的請求阻塞,一直等到網絡恢復正常,Server1
    被修改的數據同步更新到 Server2 之後,即 DB2 中數據 a 修改成最新值 2 後,再給用戶 User2 響應。
    在這裏插入圖片描述
    第二種處理方式是,保證可用性 A,犧牲一致性 C:Server2 選擇將舊的數據 a=1 返回給用戶,等到網絡恢復,再進行數據同步。
    在這裏插入圖片描述
    除了以上這兩種方案,沒有其他方案可以選擇。可以看出:在滿足分區容錯性 P 的前提下,一致性 C 和可用性 A 只能選擇一個,無法同時滿足。

CAP 選擇策略及應用

通過上面的分析,你已經知道了分佈式系統無法同時滿足 CAP 這三個特性,那該如何進行取捨呢?其實,C、A 和 P,沒有誰優誰劣,只是不同的分佈式場景適合不同的策略。
接下來,以一些具體場景爲例,分別介紹保 CA 棄 P保 CP 棄 A保 AP 棄 C 這三種策略,以幫助你面對不同的分佈式場景時,知道如何權衡這三個特徵。
比如,對於涉及錢的交易時,數據的一致性至關重要,因此保 CP 棄 A 應該是最佳選擇。2015 年發生的支付寶光纖被挖斷的事件,就導致支付寶就出現了不可用的情況。顯然,支付寶當時的處理策略就是,保證了 CP 而犧牲了 A。
而對於其他場景,大多數情況下的做法是選擇 AP 而犧牲 C,因爲很多情況下不需要太強的一致性(數據始終保持一致),只要滿足最終一致性即可。最終一致性指的是,不要求集羣中節點數據每時每刻保持一致,在可接受的時間內最終能達到一致就可以了

保 CA 棄 P

首先,我們看一下保 CA 棄 P 的策略。在分佈式系統中,現在的網絡基礎設施無法做到始終保持穩定,網絡分區(網絡不連通)難以避免。犧牲分區容錯性 P,就相當於放棄使用分佈式系統。因此,在分佈式系統中,這種策略不需要過多討論。既然分佈式系統不能採用這種策略,那單點系統毫無疑問就需要滿足 CA 特性了。比如關係型數據庫 DBMS(比如 MySQL、Oracle)部署在單臺機器上,因爲不存在網絡通信問題,所以保證 CA 就可以了。

保 CP 棄 A

如果一個分佈式場景需要很強的數據一致性,或者該場景可以容忍系統長時間無響應的情況下,保 CP 棄 A 這個策略就比較適合。一個保證 CP 而捨棄 A 的分佈式系統,一旦發生網絡分區會導致數據無法同步情況,就要犧牲系統的可用性,降低用戶體驗,直到節點數據達到一致後再響應用戶。這種策略通常用在涉及金錢交易的分佈式場景下,因爲它任何時候都不允許出現數據不一致的情況,否則就會給用戶造成損失。因此,這種場景下必須保證 CP。保證 CP 的系統有很多,典型的有 RedisHBaseZooKeeper 等。接下來,就以 ZooKeeper 爲例,瞭解它是如何保證 CP 的。
首先,我們看一下 ZooKeeper 架構圖。
在這裏插入圖片描述
ZooKeeper 集羣包含多個節點(Server),這些節點會通過分佈式選舉算法選出一個 Leader 節點。在 ZooKeeper 中選舉 Leader 節點採用的是 ZAB 算法。在 ZooKeeper 集羣中,Leader 節點之外的節點被稱爲 Follower 節點,Leader 節點會專門負責處理用戶的寫請求

  • 當用戶向節點發送寫請求時,如果請求的節點剛好是 Leader,那就直接處理該請求;
  • 如果請求的是 Follower節點,那該節點會將請求轉給 Leader,然後 Leader 會先向所有的 Follower 發出一個Proposal,等超過一半的節點同意後,Leader 纔會提交這次寫操作,從而保證了數據的強一致性。
    具體示意圖如下所示:
    在這裏插入圖片描述
  • 當出現網絡分區時,如果其中一個分區的節點數大於集羣總節點數的一半,那麼這個分區可以再選出一個 Leader,仍然對用戶提供服務,但在選出Leader 之前,不能正常爲用戶提供服務;
  • 如果形成的分區中,沒有一個分區的節點數大於集羣總節點數的一半,那麼系統不能正常爲用戶提供服務,必須待網絡恢復後,才能正常提供服務。這種設計方式保證了分區容錯性,但犧牲了一定的系統可用性

保 AP 棄 C

如果一個分佈式場景需要很高的可用性,或者說在網絡狀況不太好的情況下,該場景允許數據暫時不一致,那這種情況下就可以犧牲一定的一致性了。網絡分區出現後,各個節點之間數據無法馬上同步,爲了保證高可用,分佈式系統需要即刻響應用戶的請求。但,此時可能某些節點還沒有拿到最新數據,只能將本地舊的數據返回給用戶,從而導致數據不一致的情況。
適合保證 AP 放棄 C 的場景有很多。比如,很多查詢網站、電商系統中的商品查詢等,用戶體驗非常重要,所以大多會保證系統的可用性,而犧牲一定的數據一致性。
以電商購物系統爲例,如下圖所示,某電吹風在北京倉庫有 20 個,在杭州倉庫有 10 個,在上海倉庫有 30 個。初始時,北京、杭州、上海分別建立的服務器{A, B, C}存儲該電吹風的數量均爲 60 個。假如,上海的網絡出現了問題,與北京和杭州網絡均不通,此時北京的用戶通過北京服務器 A 下單購買了一個電吹風,電吹風數量減少到 59,並且同步給了杭州服務器 B。也就是說,現在用戶的查詢請求如果是提交到服務器 A 和 B,那麼查詢到的數量爲 59。但通過上海服務器 C 進行查詢的結果,卻是 60。當然,待網絡恢復後,服務器 A 和 B 的數據會同步到 C,C 更新數據爲 59,最終三臺服務器數據保持一致,用戶刷新一下查詢界面或重新提交一下查詢,就可以得到最新的數據。而對用戶來說,他們並不會感知到前後數據的差異,到底是因爲其他用戶購買導致的,還是因爲網絡故障導致數據不同步而產生的。
在這裏插入圖片描述
當然,你可能會說,爲什麼上海服務器不能等網絡恢復後,再響應用戶請求呢?可以想象一下,如果用戶提交一個查詢請求,需要等上幾分鐘、幾小時才能得到反饋,那麼用戶早已離去了。也就是說這種場景適合優先保證 AP,因爲如果等到數據一致之後再給用戶返回的話,用戶的響應太慢,可能會造成嚴重的用戶流失。目前,採用保 AP 棄 C 的系統也有很多,比如 CoachDB、Eureka、Cassandra、DynamoDB 等

CAP 和 ACID 的“C”“A”是一樣的嗎?

首先,我們看一下 CAP 中的 C 和 ACID 中的 C 是否一致。

  • CAP 中的 C 強調的是數據的一致性,也就是集羣中節點之間通過複製技術保證每個節點上的數據在同一時刻是相同的。
  • ACID 中的 **C 強調的是事務執行前後,數據的完整性保持一致或滿足完整性約束。**也就是不管在什麼時候,不管併發事務有多少,事務在分佈式系統中的狀態始終保持一致。

其次,我們看一下 CAP 中的 A 和 ACID 中的 A

  • CAP 中的 A 指的是可用性(Availability),也就是系統提供的服務一直處於可用狀態,即對於用戶的請求可即時響應。
  • ACID中的 A 指的是原子性(Atomicity),強調的是事務要麼執行成功,要麼執行失敗。因此,CAP 和 ACID中的“C”和“A”是不一樣的,不能混爲一談。

總結

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章