1. 程式人生 > >不懂點CAP理論,你好意思說你是做分散式的嗎?

不懂點CAP理論,你好意思說你是做分散式的嗎?

作者簡介

CAP理論

於君澤 (高階技術專家)

螞蟻金服高階技術專家、支付核算技術部負責人. 中生代技術群發起人 。

個人感興趣的方向:高併發、分散式系統、穩定性模式;內建質量、技術型管理。

CAP理論

CAP是什麼?

CAP理論,被戲稱為[帽子理論]。CAP理論由Eric Brewer在ACM研討會上提出,而後CAP被奉為分散式領域的重要理論【link1】 。

分散式系統的CAP理論

首先把分散式系統中的三個特性進行了如下歸納:

  • 一致性(C):在分散式系統中的所有資料備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的資料副本)
  • 可用性(A):在叢集中一部分節點故障後,叢集整體是否還能響應客戶端的讀寫請求。(對資料更新具備高可用性)
  • 分割槽容忍性(P):以實際效果而言,分割槽相當於對通訊的時限要求。系統如果不能在時限內達成資料一致性,就意味著發生了分割槽的情況,必須就當前操作在C和A之間做出選擇。

高可用、資料一致性是很多系統設計的目標,但是分割槽又是不可避免的事情。我們來看一看分別擁有CA、CP和AP的情況。

CA without P:如果不要求P(不允許分割槽),則C(強一致性)和A(可用性)是可以保證的。但其實分割槽不是你想不想的問題,而是始終會存在,因此CA的系統更多的是允許分割槽後各子系統依然保持CA。

CAP理論

CP without A:如果不要求A(可用),相當於每個請求都需要在Server之間強一致,而P(分割槽)會導致同步時間無限延長,如此CP也是可以保證的。很多傳統的資料庫分散式事務都屬於這種模式。

分散式

from 同上

AP wihtout C:要高可用並允許分割槽,則需放棄一致性。一旦分割槽發生,節點之間可能會失去聯絡,為了高可用,每個節點只能用本地資料提供服務,而這樣會導致全域性資料的不一致性。現在眾多的NoSQL都屬於此類。

分散式

from 同上

CAP理論的證明

該理論由brewer提出,2年後就是2002年,Lynch與其他人證明了Brewer猜想,從而把CAP上升為一個定理。

但是,它只是證明了CAP三者不可能同時滿足,並沒有證明任意二者都可滿足的問題,所以,該證明被認為是一個收窄的結果。

Lynch的證明相對比較簡單:採用反證法,如果三者可同時滿足,則因為允許P的存在,一定存在Server之間的丟包,如此則不能保證C,證明簡潔而嚴謹

在該證明中,對CAP的定義進行了更明確的宣告

  • C:一致性被稱為原子物件,任何的讀寫都應該看起來是“原子“的,或序列的。寫後面的讀一定能讀到前面寫的內容。所有的讀寫請求都好像被全域性排序一樣。
  • A:對任何非失敗節點都應該在有限時間內給出請求的迴應。(請求的可終止性)
  • P:允許節點之間丟失任意多的訊息,當網路分割槽發生時,節點之間的訊息可能會完全丟失。

CAP理論澄清

[CAP理論十二年回顧:”規則”變了]一文首發於  Computer 雜誌,後由InfoQ和IEEE聯合呈現,非常精彩,文章表達了幾個觀點【link2】。

“三選二”是一個偽命題

不是為了P(分割槽容忍性),要在A和C之間選擇一個。分割槽很少出現,CAP在大多數時候允許完美的C和A。但當分割槽存在或可感知其影響的情況下,就要預備一種策略去探知分割槽並顯式處理其影響。

這樣的策略應分為三個步驟

  1. 探知分割槽發生
  2. 進入顯式的分割槽模式以限制某些操作
  3. 啟動恢復過程以恢復資料一致性並補償分割槽期間發生的錯誤。

“一致性的作用範圍”其實反映了這樣一種觀念,即在一定的邊界內狀態是一致的,但超出了邊界就無從談起。

比如在一個主分割槽內可以保證完備的一致性和可用性,而在分割槽外服務是不可用的。Paxos演算法和原子性多播(atomic multicast)系統一般符合這樣的場景。

像Google的一般做法是將主分割槽歸屬在單個數據中心裡面,然後交給Paxos演算法去解決跨區域的問題,一方面保證全域性協商一致(global consensus)如Chubby,一方面實現高可用的永續性儲存如Megastore。

ACID、BASE、CAP

ACID和BASE 這兩個術語都好記有餘而精確不足,出現較晚的BASE硬湊的感覺更明顯,它是“Basically Available, Soft state, Eventually consistent(基本可用、軟狀態、最終一致性)”的首字母縮寫。

其中的軟狀態和最終一致性這兩種技巧擅於對付存在分割槽的場合,並因此提高了可用性。

CAP與ACID的關係更復雜一些,也因此引起更多誤解。其中一個原因是ACID的C和A字母所代表的概念不同於CAP的C和A。還有一個原因是選擇可用性只部分地影響ACID約束。

CAP理論澄清

圖片來自創業的朱永光老師

分割槽

進一步看[分割槽]之後

用一下這張圖[引用自 link 2],在狀態S的時候是非分割槽狀態,而分割槽模式則演化出來了S1,S2,那麼問題來了,分割槽恢復之後,狀態究竟是多少呢?有幾種解決方案?

1、分割槽恢復策略:可交換多副本資料型別

注意,能支援此類處理的場景是有限的。

riak_dt 就是這樣一種保障最終一致性實現的資料結構,它分為Operation-based CRDTs、State-based CRDTs 2種形態。

riak_dt   link參見 link 3

分散式

2、分割槽恢復策略:回放合併

在分割槽恢復過程中,設計師必須解決兩個問題

分割槽兩側的狀態最終必須保持一致

並且必須補償分割槽期間產生的錯誤

如上圖所示,對於分割槽恢復的狀態S*可以通過未分割槽時的狀態S為起點,然後按順序[回放]相應的變化事件[以特定方式推進分割槽兩側的一系列操作,並在過程中一直保持一致的狀態。

Bayou[link 4]就是這個實現機制,它會回滾資料庫到正確的時刻並按無歧義的、確定性的順序重新執行所有的操作,最終使所有的節點達到相同的狀態。

對於有衝突的情況,比如版本管理軟體cvs,存在人工介入、消除衝突的處理策略。

3、有限制處理

有限制處理:[link 2]提的自動櫃員機補償問題,比較形象說明了這個問題。此問題本質上是可用性和一致性的折衷處理。

以自動櫃員機(ATM)的設計來說,強一致性看似符合邏輯的選擇,但現實情況是可用性遠比一致性重要。理由很簡單:高可用性意味著高收入。

不管怎麼樣,討論如何補償分割槽期間被破壞的不變性約束,ATM的設計很適合作為例子。

ATM的基本操作是存款、取款、檢視餘額。關鍵的不變性約束是餘額應大於或等於零。因為只有取款操作會觸犯這項不變性約束,也就只有取款操作將受到特別對待,其他兩種操作隨時都可以執行。

ATM系統設計師可以選擇在分割槽期間禁止取款操作,因為在那段時間裡沒辦法知道真實的餘額,當然這樣會損害可用性。現代ATM的做法正相反,在stand-in模式下(即分割槽模式),ATM限制淨取款額不得高於k,比如k為$200。

  • 低於限額的時候,取款完全正常;
  • 當超過限額的時候,系統拒絕取款操作。

這樣,ATM成功將可用性限制在一個合理的水平上,既允許取款操作,又限制了風險。

分割槽結束的時候,必須有一些措施來恢復一致性和補償分割槽期間系統所造成的錯誤。

狀態的恢復比較簡單,因為操作都是符合交換率的,補償就要分幾種情況去考慮。

  • 最後的餘額低於零違反了不變性約束。由於ATM已經把錢吐出去了,錯誤成了外部實在。銀行的補償辦法是收取透支費並指望顧客償還。因為風險已經受到限制,問題並不嚴重。
  • 還有一種情況是分割槽期間的某一刻餘額已經小於零(但ATM不知道),此時一筆存款重新將餘額變為正的。銀行可以追溯產生透支費,也可以因為顧客已經繳付而忽略該違反情況。

總而言之,因為通訊延遲的存在,銀行系統不依靠一致性來保證正確性,而更多地依靠審計和補償。

“空頭支票詐騙”也是類似的例子,顧客趕在多家分行對賬之前分別取出錢來然後逃跑。透支的錯誤過後才會被發現,對錯誤的補償也許體現為法律行動的形式。

此前,中行IBM大型機宕機,系統沒有第一時間切換到熱備或者異地容災上,直接影響中行的信用卡支付相關業務,直到4小時之後才恢復服務。

有對應的的原因包括日常演練等問題,等更重要的是在[可用性和一致性]之間選擇了一致性,4H之後提供服務,備庫仍然主要起資料備份的作用。

有限制處理方案是需要冒險滴,為了保障可用性,無法保障資料100%精確,可以折衷提供部分有損服務。

比如取款根據信用是不是能限制一定金額,而存款是Ok的等等。大額對公業務也可以採取具體辦法,當然這將在精細化管理服務能力及配套能力上付出更多的IT成本。

有關中行IBM大型機宕機的報道link:

超越CAP?

Nathan Marz:How to beat the CAP theorem

2011年11月Twitter的首席工程師Nathan Marz寫了一篇文章,描述了他是如何試圖打敗CAP定理的: How to beat the CAP theorem

作者表示不是要“擊敗”CAP,而是嘗試對資料儲存系統進行重新設計,以可控的複雜度來實現CAP。Marz認為一個分散式系統面臨CAP難題的兩大問題就是:

在資料庫中如何使用不斷變化的資料

如何使用演算法來更新資料庫中的資料

Marz提出了2個基本思路:

1、資料不存在update,只存在append操作。這樣就把對資料的處理由CRUD變為CR;同樣的,delete操作也可以處理為add一條新記錄。

比如 友強取消了對山丘的關注,傳統關係型資料庫是在關係表刪除一條記錄,而Marz也可以新增一條關係為[取消]的記錄。

2、所有的資料的操作就只剩下Create和Read。把Read作為一個Query來處理,而一個Query就是一個對整個資料集執行一個函式操作。

總結,在有一定時序性,且對實時一致性要求不高的場景下可以選擇使用,畢竟在問題解決域多了一把錘子。Query過程中的跨分割槽函式仍然是一種合併的變種(Merge)!

OceanBase的另類之路

既然更新資料涉及到分割槽問題,那麼能不能把更新放到一個伺服器呢[腦洞大開]?然後通過強大的軟體+硬體能力一起去保障它!同時已經不修改的資料天然具備可擴充套件性!這是我粗暴理解OceanBase的基本設計思想。

link5 寫道:作為電子商務企業,淘寶和其他公司的業務對一致性和可用性的要求高於分割槽容錯性,資料特徵是資料總量龐大且逐步增加,單位時間內的資料更新量並不大,但實時性要求很高。

這就要求我們提供一套更加偏重於支援CA特性的系統,同時兼顧可分割槽性,並且在實時性、成本、效能等方面表現良好。

OceanBase的邏輯架構簡圖

OceanBase的邏輯架構簡圖

關於UpdateServer的效能問題

其實大部分資料庫每天的修改次數相當有限,只有少數修改比較頻繁的資料庫才有每天幾億次的修改次數。另外,資料庫平均每次修改涉及的資料量很少,很多時候只有幾十個位元組到幾百個位元組。

假設資料庫每天更新1億次,平均每次需要消耗100位元組,每天插入1000萬次,平均每次需要消耗1000位元組,那麼,一天的修改量為:1億 100 + 1000萬 1000 = 20GB,如果記憶體資料結構膨脹2倍,佔用記憶體只有40GB。

而當前主流的伺服器都可以配置96GB記憶體,一些高檔的伺服器甚至可以配置192GB、384GB乃至更多記憶體。

TiDB

TiDB 是國內 PingCAP 團隊開發的一個分散式 SQL 資料庫。其靈感來自於 Google 的 F1, TiDB 支援包括傳統 RDBMS 和 NoSQL 的特性。

從下面這篇訪談來看,分散式事務早期版本中,他們參考的是 Google 的 Percolator 的模型。由於該專案還未在線上產品使用,就不具體展開了,有興趣的朋友參考下面的link6

總結:

CAP是分散式領域的重要理論,但不代表完全不能有延展的解讀和思考。另外[三選二]本身也是有條件成立的,不能簡單誤讀,一切取決於應用場景。如果不加選擇按照理論形式無異於刻舟求劍。

ps:晚上和友強、張逸大師吃飯聊起公眾號寫太長,怕是沒有人看。我揣摩如果一分為二又缺少一氣呵成的痛快,姑妄寫之,待有緣人吧!

如果大家對CAP理論感興趣,也可以加我微信一起討論:

微信

文章出處:高效運維