前言
在分布式系統領域,CAP定理可謂鼎鼎大名,各種了解和解釋頗多,今天,我也來聊聊自己的感受。
1.CAP基本概念
首先我們先描述一下CAP定理的基本概念:
1.1 一緻性
- 一緻性(Consistency):每次請求都可以得到最新的資料。
對于單機系統而言,這一點似乎很容易保證,比如我們更新的單機資料庫中的一條記錄之後無論怎麼讀取資料,都能得到更新之後的最新資料【注1】。
但是對于一個有多個節點的分布式系統,情況就沒有這麼簡單了,一般情況下節點之間的資料是通過“複制”的方式來同步的,假設你更新系統中某一節點的一條資料,對資料的更新操作會複制到系統的其他節點,但是複制有一個“視窗期”,也就是說一個節點的更新複制到另一個節點上需要一定的時間,在“視窗期”時間之内,請求到複制沒有完成的節點傳回的資料就會産生不一緻情況。
理論上,我們減小“視窗期”時間就可以降低不一緻發生的機率,但是“視窗期”永遠不可能為零,及時節點“複制”達到了光速,即使使用了量子糾纏也不能讓複制行為達到“同步”,是以完美的一緻性在這個宇宙是不存在的隻存在于神話中。
1.2 可用性
- 可用性(Availability):每次請求都可以得到一個響應,但是得到的資料不一定是最近的資料。
對于一個要持續可用的分布式系統,每一個非故障的節點必須對每一個請求作出響應。也就是說,服務使用的任何算法都必須最終終止,當同時要求分區容忍性時,這是一個很強的定義:即使是嚴重的網絡錯誤,每個請求必須終止。這裡需要注意發生故障且無法響應客戶請求的節點,并不會導緻失去這裡所述的“可用性”。
可用性保證每次請求都能得到響應,即使某些節點挂了,系統依然要響應請求。這點對于分布式系統至關重要,我們不能保證系統中衆多節點在運作期間整體可用,總會出現不可用的情況,比如節點崩潰、節點資料異常、節點連接配接逾時等,是以我們要保證某些節點不可用的情況下系統依然可用。
雖然我們可用通過添加節點避免單點故障來提升整個系統的可用性,但是顯而易見的是我們依然不能保證100%可用,我們隻能提升小數點後九的位數(99.999...%)。是以絕對的可用是無法達到的,話說隻要一說“絕對”好像都不靠譜。
1.3 分區容錯性
- 分區容錯性(Partition tolerance):當系統中有節點因網絡原因無法通信時,系統依然可以繼續運作。
分布式系統由多個節點組成,節點間的網絡通信總是不可靠的,所有我們總是要保證分布式系統節點間産生網絡分區的情況下,這樣分布式系統才有存在的意義。
這裡需要注意的是,節點故障是否屬于網絡分區?這個問題确實容易讓人産生誤解。假設我們有一個由三個節點組成的系統,其中一個節點down了,如果我們認為這種情況下産生了網絡分區,那麼我們隻能在可用性和一緻性做出選擇。但是我們發現根據可用性定義,故障的節點并不影響可用性,而且故障節點已經不會相應任何請求了,其他節點可以很容易地進行補償(例如主備模式),彌補了可用性和一緻性。是以如果節點故障屬于網絡分區,我們就得到一個違背CAP定理的結論——C、A、P我們可以同時保證。至此我們從反面證明了網絡故障不屬于網絡分區。
2、可用性和容錯性的差別
可用性容易與分區容錯性相混淆,這兩個概念都是保證系統可以持續對外提供服務。但是兩個概念側重點不同,可用性是保證系統中某些節點故障的情況下系統可用,而分區容錯性是保證系統出現網絡分區即某些節點互相通信失敗的情況下,系統依然可用。
3、CAP三者取舍
CAP定理定義了這三個屬性之間的互相關系:根據定理,分布式系統隻能滿足三項中的兩項而不可能滿足全部三項。這樣就産生三種取舍情況:CA(withoutP)、CP(withoutA)、AP(withoutC)。
- CA:這種情況相當于隻滿足可用性和一緻性,不在滿足分區容錯性。在現實世界中,分布式系統中節點之間的網絡異常不可避免。是以如果不保證分區容錯性,除非節點間永不發生網絡異常,顯然這樣不可能【注2】。或者極端一點的假設是不存在網絡通信,那整個節點就變成單一節點的單機系統。是以,單純的CA分布式系統并不存在。
現在網際網路應用的場景中,系統節點衆多,節點之間網絡異常不可避免,是以要保證系統正常運作必須保證分區容錯性,那根據CAP定理,就要在可用性和一緻性間做出選擇,從實際業務的角度上,這種選擇更像是一種權衡,就是說我們更傾向于可用性多一些或者更傾向于一緻性多一些。
CP:當系統中出現網絡分區的情況時,我們選擇保證一緻性,這時請求就會等待,直到問題節點網絡異常恢複。
傳統資料庫的分布式事務大多采用這種模式,衆所周知XA事務最大的缺點就在于逾時問題上,如果對一個資料總管的事務送出操作因為網絡原因等待,參與分布式事務其他節點都需要等待延時節點,緻使系統變慢并最終導緻不可用。
AP:當系統出現網絡分區的情況時,我們選擇保證可用性,此時所有節點可以正常響應請求,但由于節點之間不能互相聯絡,節點隻能用本地資料響應請求,此時節點間本地資料有可能不同步,為了保證高可用,我們放棄了一緻性
。
MySQL主從複制模式就是典型的AP應用場景,當主從節點産生網絡延時緻使從節點資料不能及時同步時,我們無需等待網絡恢複,依然可以繼續通路從節點擷取資料。
總結
在當下網際網路業務場景中,我們更多的時候是在可用性和一緻性之間做出選擇。在實際的業務中,單純保證一緻性不但無法達到也會嚴重的影響系統的效率,同理可證,想要絕對的可用性也是不現實的,任何業務流程也不會允許大量的非一緻性資料出現,這樣就造成了整體業務邏輯失敗。分布式系統的可用性和一緻性就如同太極陰陽兩面,你中有無,我中有你,作為架構師我們努力在業務場景尋找一緻性和可用性的平衡點,對于架構師而言,“最”不重要,“合适”更好。
備注:
注1:單機系統依然會産生一緻性問題,隻是與CAP中一緻性不是一個問題,故不在此展開。
注2:這裡還有一種情況比較特殊,這種情況是“CA”叢集,這種叢集極少發生網絡分區情況,且一旦發生分區,就讓分區中的所有節點停止工作。這樣操作相當于舍棄了有問題節點,系統中其他正常節點就不存在分區問題了。我們并沒有在存在分區的系統中繼續工作,我們很粗暴的解決了網絡分區的情況。從某種意義上說,這是可以實作的,但是實作起來非常麻煩,需要實時監控系統中節點是否出現網絡分區并且在一旦出現分區即可将分區内的節點全部下線。說到這裡你一定有一個疑惑,如果我們将節點下線的難道不會影響CA中的可用性嗎?根據可用性定義可知,故障節點并不影響可用性,因為可用性需要保證非故障節點有響應。是以此處将分區節點下線以解決分區問題并不會影響可用性。