簡單聊聊對 CAP 的理解
簡單聊聊對 CAP 的理解
作者:青藤木鳥https://www.qtmuniao.com/2020/02/08/CAP/, 轉載請註明出處小引
曾經在一個面試中讓談談對 CAP 的理解,當時憑著準備面試時谷歌到的N手資料,類似於小學生背書一樣,生擠出隻言片語。面試官無奈笑笑,簡練的概括出他想要聽到的要點,聽的我心下慚愧。面試自然是掛了,後來工作時偶爾接觸到這個詞彙,初不得要領,後通過不同資料的多側面理解、印證,漸漸拼湊出了一個輪廓,在這裡梳理下,將影子攆到紙上,以供日後索引。
面試官大約是這麼概括的:在分散式系統中,失敗必然的,分割槽容錯(P)是一定需要的,因此設計系統時需要在可用性(A)和一致性(C)間進行權衡
歷史
埃裡克·布魯爾(因此 CAP 定理又被稱為Brewer's theorem)大約在 1998 年就有了相關想法,然後在 1999 年作為一個原則將其發表出來,並且最終在 1999 年的 PODC 上作為一個猜想將其正式提出。2002 年,MIT 的Seth Gilbert和Nancy Lynch做了一個非常收束條件下的證明,使其成為一個定理。
這裡想說明的一點是,說到CAP 原則(類似的意思,這個是我隨口起的),可以是定義模糊、涵蓋廣泛、啟發分散式系統設計的原則。但是CAP 定理,則屬於每個性質都有嚴格數學定義、結論有完善推導的理論計算科學範疇的概念。大多數人想表達前者的意思,但用的卻是後者的術語。DDIA 作者
概念
CAP 原則指出,一個分散式儲存系統提供的服務,不可能同時滿足以下三點:
- 一致性(Consistency):對於不同節點的請求,要麼給出包含最新的修改響應、要麼給出一個出錯響應。
- 可用性(Available):對於每個請求都會給出一個非錯響應,但是不保證響應資料的時效性。
- 分割槽容錯性(Partition tolerance):當系統中節點間出現網路分割槽時,系統仍然能夠正常響應請求。
一致性
分散式系統通常會包含多個數據節點(Data Server),將一個數據集(Dataset)分散到多個節點的方法常用的有三種:
- 冗餘(Replication
- 分片(*Partition*):將資料集切成大小合適的幾個分片,分別存到不同的節點上。
- 冗餘和分片:將資料集切成多片,每片又冗餘多份,將其存放到不同節點上。
正是資料的多機冗餘和分片引出了分散式系統中的一致性問題。舉個例子簡單說明:
拿只有資料冗餘的策略來說,假設有S0、S1、S2三個節點組成的一個數據系統,分別存放了某資料集 D 的三個副本D0、D1、D2,該資料集是個簡單的鍵值對(Key-value)集合。初始,集合中"a" = 0
;某時刻 t,客戶端 C0 向S0傳送寫請求set("a", 1)
,並得到成功響應;緊跟 t 之後,另一個客戶端C1向S1傳送了一個讀請求get("a")
,
如果系統如下設計:
- S0改變了 "a" 的值,並且將其同步到了S1,C1得到帶有全域性最新資料
"a" = 1
的響應 - 系統檢測到S1尚未同步最新資料,於是返回給C1一個出錯響應,提示其進行重試
則該系統滿足一致性。
可用性
這個性質比較好理解,即系統必須在有限的時間內給出非錯響應。如果響應時間超過可以容忍時間的幾個數量級,那麼該服務基本不可用。系統反應很快,但是給出了一個出錯響應,比如 bad file、data stale 等等,則服務仍然不可用。
分割槽容錯性
這個性質曾經比較困擾我(當然現在也是),尚給不出一個精確的描述,只能從幾個側面來刻畫一下。
這裡面的難點在於如何理解網路分割槽(network partitioning)。
首先,一個簡單的理解是,系統中的一部分和其他部分發生了網路隔離,互相不能夠通訊,進而不能及時完成資料同步,則可認為發生了網路分割槽。如上述例子中 {S0} 和 {S1,S2} 出現了網路隔離,則 {S0} 中更新的資料不能夠及時同步給 {S1,S2},則 {S0} 和 {S1,S2} 間產生了分割槽。
然後,將限制進一步放寬,如果系統中存在兩個部分,其間通訊時延很大,不能夠在時限內進達成一致,則仍然可認為發生了網路分割槽。如上述例子中 {S0} 更新了資料後,尚未同步給 {S1,S2} 時,C1的請求來了,使用者仍會訪問到舊的資料,也可認為{S0} 和 {S1,S2} 間產生了分割槽。
如果發生了網路分割槽以後,系統仍然能夠正常提供服務,則該系統具有分割槽容錯性。
抽象來說,如果系統元件之間的通訊時延大於系統事件(比如說系統請求)的間隔,則會發生網路分割槽,這也是分散式系統的常態。極端情況下,如果某個系統執行在單機上,但是該機器內記憶體和 CPU 等元件間的通訊延遲很高,則該單機可以認為是分散式系統,並且發生了網路分割槽。
以上都是從實踐角度的一些理解,理論上嚴格定義的發生網路分割槽的條件更為苛刻,後面會詳細說明。
解釋
在一個分散式系統中,網路故障和節點宕機是常態,因此網路分割槽是一定會出現的。在網路分割槽發生時,根據業務需求,系統需要在可用性和一致性之間二選一:
- 優先保證可用性。網路分割槽使得有些節點不可達,導致系統不能夠及時同步資料。儘管被請求節點試圖返回其可見範圍內的最新資料,但仍不能保證該資料是全域性最新的,即犧牲一致性保證可用性。
- 優先保證一致性。網路分割槽使得被請求節點不能夠保證資料全域性最新時,返回一個出錯資訊給使用者;或者什麼也不返回,直到客戶端超時。
但該原則並不是說,系統在任何時候都必須拋棄 CAP 中的一個。比如系統網路狀況良好時,並不需要在三者間進行取捨,此時不需要分割槽容錯,可兼顧一致性和可用性。
深入理解這個原則,需要把握分散式系統設計時的兩個關鍵因素:網路環境和業務需求。尤其要注意,原則是理想情況下的斷言,而實踐是實際場景下的取捨,CAP 原則只是點出了系統設計的三個重要權衡方向。
網路環境,不同系統所要面對網路環境變化範圍極大。
如果網路狀況很好,網路分割槽很少發生,則系統設計時不需要過分考慮分割槽容錯,比如可以簡單設計為出現分割槽就停止服務,稍好一點可以讓客戶端進行重試等等。看起來似乎犧牲了可用性,但要注意到這種情況很少發生,所以系統可能仍然能提供N個9的服務。
如果網路狀況堪憂、叢集規模很大,網路不通、節點宕機是常態,則系統設計時需認真考慮出現網路分割槽時可用性和一致性間的取捨。這裡在此強調一下,CAP定理中的分割槽容錯性是非常理想化的:
網路將被允許任意丟失從一個節點發送到另一節點的許多訊息 —— Gilbert,Lynch
如果網路任意丟失節點間的訊息,經典的分散式共識協議(如 Paxos 和 Raft)將不能穩定的選出主節點並正常提供服務;工業級別的分散式資料庫系統(如 HBase 和 Dynamo)也將不能多機同步資料,正常的提供儲存服務。但是一般系統所面對的網路環境都沒有這麼糟糕,我們因此可以通過共識演算法、冗餘備份等手段兼顧某種程度的可用性和一致性。
而這個某種程度,就是接下來要談的業務需求。
業務需求,針對不同業務場景偏好進行設計,就產生了市面上形形色色的分散式系統中介軟體。在這裡,我們只考慮可用性和一致性兩個需求。這裡也要再次強調,在實際情況中,大部分系統都不會要求原子一致性(atomic consistency)或者完全的可用性(perfect availability) 。
對一致性來說,根據需求可以有強一致性、弱一致性、最終一致性等選擇。工業級別的系統,為了提供高可用性,在業務可以容忍的範圍內,往往選擇弱一致性或者最終一致性,如 Amazon 的鍵值對儲存Dynamo。
對可用性來說,需要考慮系統使用者請求的多少、併發的高低、資料的大小等等方面來定義系統的可用性。任何系統都不可能做到全場景覆蓋,只要系統設計能夠滿足其面向的業務場景,就可以說系統滿足可用性需求。比如一個私有云物件儲存服務,所面對的流量可能只是每天幾十 QPS、讀遠大於寫、併發很低;在這個情況下,我們完全可以在把系統設計的不是很複雜的基礎上兼顧一致性和可用性。但如果把該服務用作淘寶雙十一等場景,那系統可用性基本上就沒有了。
結語
CAP 在分散式系統未火熱之時,歸納出了分散式系統設計中所需權衡重要的方向。在雲端計算、大資料如火如荼的今天,一大批優秀的分散式系統湧現出來,給了我們更多的系統設計的考慮方向和實踐細節。如今做系統設計時,不用太過拘泥於 CAP 原則,根據所面對業務場景,大膽進行取捨即可。
推薦閱讀
下面這些材料給我寫本文啟發很大,大家不妨一讀。
- 維基百科, CAP theorem:
- 分散式小冊,Distributed Systems for Fun and Profit:
- Jeff Hodges,寫給分散式系統初學者的筆記:
- Coda Hale不應該放棄分割槽容錯性 :
- 左耳朵耗子推薦,分散式系統架構經典資料:
- Martin Kleppmann大佬的 DDIA,資料儲存處理相關的高屋建瓴之做,力薦: