1. 程式人生 > >【分散式系統工程實現】CAP理論及系統一致性

【分散式系統工程實現】CAP理論及系統一致性

印象中CAP理論開始流行是從Amazon Dynamo的論文開始的,Amazon的CTO還在他的部落格中介紹了最終一致性的概念,從此以後,各種會議和交流中都少不了CAP的影子。然而,對於分散式系統工程設計和開發來說,CAP意味著什麼呢?

CAP 理論由 Berkerly 的 Brewer 教授提出,三者的含義如下:

  • 一致性 ( Consistency) :任何一個讀操作總是能讀取到之前完成的寫操作結果;
  • 可用性 ( Availability) :每一個操作總是能夠在確定的時間內返回;
  • 分割槽可容忍性 (Tolerance of network Partition) :在出現網路分割槽的情況下,仍然能夠滿足一致性和可用性;

CAP 理論認為,三者不能同時滿足,並給出了證明,簡單闡述如下:假設系統出現網路分割槽為 G1 和 G2 兩個部分,在一個寫操作 W1 後面有一個讀操作 R2 , W1 寫 G1 , R2 讀取 G2 ,由於 G1 和 G2 不能通訊,如果讀操作 R2 可以終結的話,必定不能讀取寫操作 W1 的操作結果。

由於CAP三者無法同時滿足,Amazon Dynamo論文中引入了使用者可配置的NWR策略,在CAP三個特性中作出權衡。比如N=3, W=3, R=1強調一致性;N=3, W=1, R=1強調可用性;N=3, W=2, R=2是一種折衷的策略。另外,還有一些NOSQL系統把CAP理論當成一種藉口,認為既然我們不能同時滿足一致性和可用性,那NOSQL系統就犧牲一致性。這些說法本身雖然不能說有錯,但我們至少需要思考兩個問題:

  1. CAP理論在工程的角度意味著什麼?
  2. 一致性的具體含義?

筆者認為,最初的CAP理論只是粗略地告訴我們”天下沒有免費的午餐”,對於NOSQL系統設計指導意義不大。原始的CAP理論描述有如下缺陷:

  1. 缺少時間因素。比如對於可用性描述,10s中停服務和1個小時停服務完全是兩個概念,只停寫服務和同時停讀寫服務的影響也是很不一樣的。
  2. 一致性描述問題。每個讀操作雖然能夠讀取到之前寫操作結果,但是假設某些寫操作發生在機器A,某些寫操作發生在機器B,一致性依賴於對機器A和機器B上寫操作的合併,操作的順序是無法保證的。比如Dynamo&Cassandra系統中由於可能出現同一個<key, value>對被多個節點同時修改的情形,即使在NWR策略中配置W + R > N,也需要依賴衝突合併來保證一致性,這從理論上是沒有完美做法的。
  3. 網路分割槽描述過於模糊。工程上容易出現的網路問題一般是機房之間網路不通,某個機房停電,某臺機器故障或者某些機器因為機架電源或者交換機的原因發生故障。單個機器故障也可以認為是網路分割槽,但這和機房網路不通對系統設計帶來的挑戰差別是很大的。

一般可以認為:工程上網路分割槽總是存在,比如機器故障或者網路異常,一致性和可用性不能同時滿足。且工程上從來不要求絕對的一致性或者可用性,而是尋求一種平衡,可以將一致性和可用性分別重定義為HarvestYield

  • Harvest (對應一致性):percent of required data actually included in the responses (請求結果的真實程度);
  • Yield (可用性):percent of requests answered successfully (成功請求佔的百分比);

CAP理論可以演化為在工程上尋找一種方法,在”成功請求佔的百分比”和”請求結果的真實程度”之間取得一個權衡,詳細描述可以參考Coda的部落格。然而,這個描述仍然不夠具體,下面我們就有總控節點的系統(如GFS+Bigtable)和P2P系統(如Amazon Dynamo)兩類系統的CAP含義分別進行說明。

首先我們必須明確一致性的概念。NOSQL系統經常提到最終一致性模型:假如客戶端A寫入一個值到儲存系統,客戶端B最終總是能夠讀取到A寫入的最新值,這裡有一個時間視窗,依賴於互動延遲,系統負載以及複製技術中的replica的個數。Amazon CTO宣稱Dynamo為最終一致性系統,然而,這裡的最終一致性具有很大的欺騙性,因為雖然客戶端B能夠讀到其它客戶端寫入的所有資料,但是可能出現多個節點更新同一個值的情況,需要依賴衝突合併來解決多機操作順序問題。後續的文章中,我們都會把Amazon Dynamo這種需要依賴操作合併,可能會丟失資料的模型從最終一致性模型中排除出去。最終一致性模型要求同一份資料同一時刻只能被一臺機器修改,也就是說機器宕機時需要停很短時間寫服務。

對於帶有總控節點的系統,將CAP理論的定義做出適當的調整如下:

  • 一致性:讀操作總是能讀取到之前完成的寫操作結果,且不需要依賴於操作合併
  • 可用性:讀寫操作總是能夠在很短的時間內返回,即使某臺機器發生了故障,也能夠通過其它副本正常執行,而不需要等到機器重啟或者機器上的服務分配給其它機器以後才能成功;
  • 分割槽可容忍性:能夠處理機器宕機,機房停電或者出現機房之間網路故障等異常情況;

帶有總控節點的NOSQL系統一般是最終一致性系統,允許機器宕機時停止很短時間,比如10s的部分資料寫服務,但是不允許停讀服務,且服務恢復時間越短越好。大多數NOSQL系統都是對一份資料保留多個備份,同一時刻只有一個備份為主,提供寫服務,其它備份為輔,同步主備份的寫操作,所有的備份都可以提供讀取服務,且主備份提供保證強一致性的讀服務。當主備份所在機器發生故障時,需要等一段時間才能由原來的輔備份接替主備份提供寫服務。

類似Amazon的P2P去中心化系統提供需要依賴衝突合併的一致性,比如Cassandra中的“last write wins”衝突合併策略,雖然並不完美但確實能夠解決很多問題。這樣的系統能夠通過使用者配置NWR策略來權衡一致性和可用性,可以做到單臺機器宕機時讀寫服務都不停止。

最後,再次提醒大家設計系統時:不要過分迷戀CAP,認清最終一致性,理智對待NWR。