CAP 理論是分散式系統設計中的一個重要理論,雖然它為系統設計提供了非常有用的依據,但是也帶來了很多誤解。本文將從 CAP 誕生的背景說起,然後對理論進行解釋,最後對 CAP 在當前背景下的一些新理解進行分析,澄清一些對 CAP 的誤解。
CAP 理論誕生的背景
CAP 理論的是在“資料一致性 VS 可用性”的爭論中產生。CAP 的作者 Brewer 在 90 年代的時候就開始研究基於叢集的跨區域系統(實質上是早期的雲端計算),對於這類系統而言,系統可用性是首要目標,因此他們採用了快取或者事後更新的方式來優化系統的可用性。儘管這些方法提升了系統的可用性,但是犧牲了系統資料一致性。
Brewer 在 90 年代提出了 BASE 理論(基本可用、軟狀態、最終一致性),這在當時還不怎麼被接受。因為大家還是比較看重 ACID 的優點,不願意放棄強一致性。因此,Brewer 提出了 CAP 理論,目的就是為了開闊分散式系統的設計空間,通過“三選二”的公式,解放思想,不要只抓著一致性不放。
理解了 CAP 誕生的背景,我們才能更加深入的理解 CAP 理論,以及它帶來的啟示。“三選二”的觀點雖然幫助大家開拓了設計思路,但是也帶來了很多誤解。下面我們會逐一分析,首先來看一下 CAP 理論的解釋。
CAP 理論的經典解釋
CAP 定理是分散式系統設計中最基礎,也是最為關鍵的理論。它指出,分散式資料儲存不可能同時滿足以下三個條件。
- 一致性(Consistency):每次讀取要麼獲得最近寫入的資料,要麼獲得一個錯誤。
- 可用性(Availability):每次請求都能獲得一個(非錯誤)響應,但不保證返回的是最新寫入的資料。
- 分割槽容忍(Partition tolerance):儘管任意數量的訊息被節點間的網路丟失(或延遲),系統仍繼續執行。
CAP 定理表明,在存在網路分割槽的情況下,一致性和可用性必須二選一。當網路發生分割槽(不同節點之間的網路發生故障或者延遲較大)時,要麼失去一致性(允許不同分割槽的資料寫入),要麼失去可用性(識別到網路分割槽時停止服務)。 而在沒有發生網路故障時,即分散式系統正常執行時,一致性和可用性是可以同時被滿足的。這裡需要注意的是,CAP 定理中的一致性與 ACID 資料庫事務中的一致性截然不同。ACID 的 C 指的是事務不能破壞任何資料庫規則,如鍵的唯一性。與之相比,CAP 的 C 僅指單一副本這個意義上的一致性,因此只是 ACID 一致性約束的一個嚴格的子集。
CAP 理論看起來難理解,其實只要抓住一個核心點就能推匯出來,不用死記硬背。在出現網路分割槽的時候,
- 如果系統不允許寫入,那麼意味著降低了系統的可用性,但不同分割槽的資料能夠保持一致,即選擇了一致性。
- 如果系統允許寫入,那麼意味著不同分割槽之間的資料產生不一致,系統可用性得到保障,即選擇可用性。
CAP 的新理解
CAP 經常被誤解,很大程度上是因為在討論 CAP 的時候可用性和一致性的作用範圍往往都是含糊不清的。如果不先定義好可用性、一致性、分割槽容忍在具體場景下的概念,CAP 實際上反而會束縛系統設計的思路。首先,由於分割槽很少發生,那麼在系統不存在分割槽的情況下沒什麼理由犧牲 C 或 A。其次,C 與 A 之間的取捨可以在同一系統內以非常細小的粒度反覆發生,而每一次的決策可能因為具體的操作,乃至因為牽涉到特定的資料或使用者而有所不同。最後,這三種性質都可以在程度上都可以進行度量,並不是非黑即白的有或無。可用性顯然是在 0% 到 100% 之間連續變化的,一致性分很多級別,連分割槽也可以細分為不同含義,如系統內的不同部分對於是否存在分割槽可以有不一樣的認知。
什麼是分割槽容忍
在現實世界中,正常情況下分散式系統各個節點之間的通訊是可靠的,不會出現訊息丟失或者延遲很高的情況,但是網路是不可靠的,總會偶爾出現訊息丟失或者訊息延遲很高的情況,這個時候不同區域的節點之間在一段時間內就會出現無法通訊的情況,也就是發生了分割槽。
分割槽容忍就是指分散式系統在出現網路分割槽的時候,仍然能繼續執行,對外提供服務。 注意,這裡所說的仍然能夠對外提供服務跟可用性的要求不一樣,可用性要求的是對於任意請求都能得到響應,意味著即使出現網路分割槽所有節點都能夠提供服務。而分割槽容忍的重點在於出現網路分割槽之後,系統仍然是可用的(包括部分可用)。
舉個例子:使用 Paxos 進行資料複製的系統就是典型的 CP 系統,即使出現網路分割槽,主分割槽也能夠提供服務,所以它是分割槽容忍的。再舉個反例:使用 2PC 進行資料複製的系統沒有分割槽容忍的特性,當出現網路分割槽時,整個系統都會阻塞。
可用性的範圍
可用性其實很直觀:每次請求都能獲得一個(非錯誤)響應,但不保證返回的是最新寫入的資料。換一個說法就是對於分散式系統中的每個節點,都能夠對外部請求做出響應,但不要求一致性。
經常讓我們疑惑的問題是衡量系統可用性的標準是什麼?其實關鍵點在於可用性的範圍,脫離了具體場景下的可用性範圍是沒有意義的。討論可用性是要有具體場景來劃分邊界的,簡單的認為某個演算法是滿足可用性要求其實並不嚴謹,因為在工程實現中會有很多的技巧去彌補修正。
舉個例子:谷歌文件就是非常典型的 AP 系統,它在網路斷了的情況下也能夠使用。訣竅在於它在發現網路斷了之後會進入離線模式,允許使用者繼續進行編輯,然後在網路恢復之後再對修改的內容進行合併處理。可以發現對於谷歌文件來說,使用者的瀏覽器也是它系統的一個節點,當出現網路分割槽時,它仍然能夠為使用者提供服務,但是代價是放棄了一致性,因為使用者做的修改只有本地知道,服務端是不清楚的。所以在這個例子裡面,可用性的範圍是包括了使用者瀏覽器在內的,不是我們常規理解的分散式系統的節點一定就是服務端的機器。
值得注意的是在現實世界中,我們一般不會去追求完美的可用性,所以一般的說法是高可用,即儘可能保證更多的節點服務可用。這也是為什麼 Paxos 這類的一致性演算法越來越流行的原因之一。
一致性的範圍
討論一致性的時候必須要明確一致性的範圍,即在一定的邊界內狀態是一致的,超出邊界之外的一致性是無從談起的。 比如 Paxos 在發生網路分割槽的時候,在一個主分割槽內可以保證完備的一致性和可用性,而在分割槽外服務是不可用的。值得注意的是,當系統在分割槽的時候選擇了一致性,也就是 CP,並不意味著完全失去了可用性,這取決於一致性演算法的實現。比如標準的兩階段提交發生分割槽的時候是完全不可用的,而 Paxos 則保證了主分割槽的一致性和可用性。
經過上面的討論可以發現,可用性的範圍要求比一致性的範圍要求要更嚴格,CAP 理論中的可用性要求的是整個系統的可用性,即使出現部分節點不可用也算是違反了可用性約束。而一致性的要求則沒有那麼高,發生網路分割槽的時候只要保證主分割槽資料一致性,也認為系統是符合一致性約束的。為什麼這麼說呢?因為當出現網路分割槽的時候,客戶端只要通過訪問主分割槽就能得到最新的值(訪問超過半數以上節點,如果值都相同說明訪問的資料是最新的),此時系統是滿足 CAP 理論中一致性的要求的。
管理分割槽
網路分割槽是分散式系統中必然發生的事情,經典的 CAP 理論是忽略網路延遲的,但是在現實世界中,網路延遲跟分割槽密切相關。也就是說當系統在有限的時間內無法通訊達成一致(網路延遲很高),就意味著發生了分割槽。此時就需要在一致性和可用性之間做出選擇:選擇繼續重試就意味著選擇一致性,放棄可用性;放棄資料一致性讓操作完成就意味著選擇了可用性。值得注意的是在分割槽的時候放棄資料一致性並不是意味著完全不管,一般工程實現會採用重試的方式達到最終一致性。
通過上面的分析可以發現,平衡分割槽期間可用性和一致性的影響是分散式系統設計中的關鍵問題。因此,管理分割槽不僅是需要主動發現分割槽,還需要針對分割槽期間產生的影響準備恢復過程。也就是說我們可以從另一個角度來應用 CAP 理論:系統進入分割槽模式的時候,如何在一致性和可用性之間做出選擇。
管理分割槽有三個步驟:
- 檢測到分割槽開始
- 明確進入分割槽模式,限制某些操作
- 當通訊恢復後啟動分割槽恢復過程
當系統進入分割槽模式之後,有兩種選擇:
- 選擇一致性:例如 Paxos 演算法,只有大多數的主分割槽能夠進行操作,其他分割槽不可用,當網路恢復之後少數節點跟多數節點同步資料。
- 選擇可用性:例如谷歌文件,出現分割槽時進入離線模式,等網路恢復了客戶端跟服務端資料進行合併恢復。
總結
理論抽象於現實,服務於現實,但絕不等於現實。對 CAP 理論“三選二”的誤解就源於我們經常把理論等同於現實。 CAP 的誕生主要是為了拓寬設計思路,不要侷限在強一致性的約束中。簡單的把“三選二”進行套用反而限制了設計思路。在現實世界中,不同業務場景對可用性和一致性的要求不一樣,並且一致性和可用性的範圍和區間是動態變化的,並不是非此即彼。因此,準確理解 CAP 理論,從管理分割槽的角度出發,結合具體的業務場景,才能做出更好的系統設計。