CAP定理是分佈式系統中的一個基本定理,它指出任何分佈式系統最多可以具有以下三個特性中的兩個:
- Consistency(一致性)
- Availability(可用性)
- Partition tolerance(分區容錯)
1. 分佈式系統
考慮一個非常簡單的分佈式系統。系統由兩臺服務器組成G1和G2,這兩臺服務器跟蹤相同的變量v,v的初始值爲v0。G1和G2相互間可以通信,同時也與第三方客戶端通信。系統結構如下:
客戶端可以向任何服務器發起讀寫請求。當一個服務器收到請求後,它執行計算並向客戶端返回響應。請求寫的過程如下:
請求讀的過程如下:
2. 一致性
一致性的意思是
任何寫操作之後的讀操作,必須返回該值
在一致性系統中,一旦一個客戶端成功的向任何服務器寫入值後,它期望能夠從任何一個服務器獲得該值(或最新的值)。
如下圖是一個非一致性系統的例子,客戶端向G1成功寫入v1,但當客戶端從G2讀取v的值時,其獲得的結果是v0。
一個一致性系統如下圖所示,G1會先將v值複製給G2,再向客戶端響應寫入結果,當客戶端從G2讀取值時,其獲得的是最新的值v1。
3. 可用性
可用性的意思是
系統中的非故障節點必須爲接收到的每個請求產生一個響應
在一個可用系統中,如果客戶端向任何一個服務器發送請求,只要服務器沒有崩潰,服務器最終必須產生一個響應,而不能忽略該請求。
如用戶可以選擇向 G1 或 G2 發起讀操作,不管是哪臺服務器,只要收到請求,就必須告訴用戶,到底是 v0 還是 v1,否則就不滿足可用性。
4. 分區容錯
分區容錯的意思是
網絡允許丟棄節點間傳遞的任意多個消息(the network will be allowed to lose arbitrarily many messages sent from one node to another)
大多數分佈式系統都分佈在多個子網絡,每個子網絡就叫做一個區(partition)。分區容錯的意思是,區間通信可能失敗。比如,一臺服務器放在中國,另一臺服務器放在美國,這就是兩個區,它們之間可能無法通信。如果所有的通信都被丟棄,系統如下圖所示。
在一個支持分區容錯的系統中,我們的系統必須能夠在任意網絡分區的情況下正常工作。
5.證明
接下來證明一個系統不能同時滿足這三個特性。
假設存在一個系統同時具有一致性、可用性和分區容錯性。首先對系統進行劃分,結果如下:
接下來,客戶端向G2服務器請求寫v1,因爲系統是可用的,故G2會返回響應。但是因爲網絡被隔離,G2無法向G1同步更新v1。
最後,客戶端會向G1和G2分別請求v的值,因爲系統是可用的,G1和G2會分別返回v0和v1,導致了不一致。
因爲我們假設存在一個系統具有一致性、可用性和分區容錯性,但是我們證明了對於任何這樣的系統都存在一種情況導致系統的不一致性。因此,不存在一個同時滿足這三個特性的系統。
6. 一致性和可用性間的矛盾
一致性和可用性,爲什麼不可能同時成立?從上述的證明可以看到因爲通信可能會失敗(即出現分區容錯)。
如果保證 G2的一致性,那麼 G1必須在寫操作時,鎖定 G2 的讀操作和寫操作。只有數據同步後,才能重新開放 G2讀寫。鎖定期間,G2 不能讀寫,沒有可用性。
如果保證 G2 的可用性,那麼勢必不能鎖定 G2,所以一致性不成立。
綜上所述, G2 無法同時做到一致性和可用性。系統設計時只能選擇一個目標。如果追求一致性,那麼無法保證所有節點的可用性;如果追求所有節點的可用性,那就沒法做到一致性。
如果一個系統同時支持可用性和一致性,一般這個系統是非分佈式系統。
舉例來說,發佈一張網頁到 CDN,多個服務器有這張網頁的副本。後來發現一個錯誤,需要更新網頁,這時只能每個服務器都更新一遍。一般來說,網頁的更新不是特別強調一致性。短時期內,一些用戶拿到老版本,另一些用戶拿到新版本,問題不會特別大。當然,所有人最終都會看到新版本。所以,這個場合就是可用性高於一致性。
參考
- https://mwhittaker.github.io/blog/an_illustrated_proof_of_the_cap_theorem/
- http://www.ruanyifeng.com/blog/2018/07/cap.html