1. CAP 的由來
要理解 CAP,首先我們要清楚,為何會有人提出 CAP?他提出 CAP 是為了解決什么問題?
時間回到 1985 年,彼時,后來證明了 CAP 理論的 Lynch 教授此時給當時的 IT 界來了一記驚雷:
她通過不可辯駁的證明告訴業界的工程師們,如果在一個不穩定(消息要么亂序要么丟了)的網絡環境里(分布式異步模型),想始終保持數據一致是不可能的。
這是個什么概念呢?就是她打破了那些既想提供超高質量服務,又想提供超高性能服務的技術人員的幻想。
這本質是在告訴大家,在分布式系統里,需要妥協。
但是,如何妥協?分布式系統里到底應該怎么權衡這種 trade-off?
我們可以想象一下,在 CAP 定理提出之前,沒有這些方向性的指引,在設計和實施分布式系統時該有多么混亂。一套分布式系統是由多個模塊組成的,這些模塊本身可能由不同的開發人員去完成。然而,對于這些人,在公共層面,竟然沒有一個原則去指導他們該怎么完成這套功能。
比如,我們在同步兩個節點的數據時,如果發生了錯誤,到底我們應該怎么做呢?如果沒有統一的標準和方向,那很可能在一套分布式系統中的不同模塊,會出現不同的處理情況。
假設一套系統,由 A、B 兩個模塊構成。
A 模塊的設計理念是:節點間出現了問題,它可能會選擇不斷的重試,一直等到節點通信恢復。
而 B 的設計理念是:節點間出現了問題,它斷開就是了,可能最多就記錄下狀態,等以后處理。
可是,當 A、B 之間出現了通信怎么辦?那會出現 A 往 B 發請求,出問題會不斷重試。而 B 往 A 發請求,出問題則直接斷開的情況。
當然,在后面我們會說明,CAP 的理念在實際工程中,會允許這種不一致。可是,那種不一致是提前設計好和規劃好的,是根據實際數據的重要性和業務需求做的妥協,而不是這種混亂的妥協。
所以,IT 界的人們就一直在摸索,試圖找到一些綱領去指導分布式系統的設計,這一找就找了 15 年。
2000 年時,Eric Brewer 教授在 PODC 會議上提出了 CAP 理論,但是由于沒有被證明過,所以,當時只能被稱為 CAP 猜想。這個猜想引起了巨大的反響,因為 CAP 很符合人們對設計綱領的預期。
在 2002 年后,經過 Seth Gilbert 和 Nancy Lynch 從理論上證明了 CAP 猜想后,CAP 理論正式成為了分布式系統理論的基石之一。
2. CAP 到底是什么
CAP 定理表達了一個分布式系統里不可能同時滿足以下的三個特性:
2.1. C:數據一致性
什么是數據一致性?咋一看真的很讓人糊涂,一致性是什么?是指數據能一起變化,是能讓數據整齊劃一。
那么問題又來了,數據何時會變化?數據怎么才能被稱為一起變化?我們現在來回答這些問題,當我們搞清楚了這些問題,那么對數據一致性就會有了清晰的理解。
首先第一個問題,數據何時會一起變化?
答案是:僅且僅當包含數據的服務,收到數據更新請求的時候,數據才會發生變化。而數據更新請求則僅包括數據的增、刪、改這三種請求,而這三種請求又被統稱為寫請求。所以,數據只有在寫請求的時候才會發生變化。
那我們來回答第二個問題,數據要怎么樣才能被稱為一起變化了?即誰來判斷數據是最終變化了?是服務器對寫請求的返回結果嗎?告訴寫請求成功,數據就一定發生一致性變化了?
NO,數據發生變化是否一致是需要經過讀請求來做檢驗的。那么讀請求判斷的依據是什么呢?
假設,我們的分布式存儲系統有兩個節點,每個節點都包含了一部分需要被變化的數據。如果經過一次寫請求后,兩個節點都發生了數據變化。然后,讀請求把這些變化后的數據都讀取到了,我們就把這次數據修改稱為數據發生了一致性變化。
但是,這還不是完整的一致性。因為系統不可能永久的正常運行下去。
如果系統內部發生了問題從而導致系統的節點無法發生一致性變化會怎么樣呢?當我們這樣做的時候,就意味著想看到最新數據的讀請求們,很可能會看到舊數據,或者說獲取到不同版本的數據。此時,為了保證分布式系統對外的數據一致性,于是選擇不返回任何數據。
這里需要注意一下,CAP 定理是在說在某種狀態下的選擇,和實際工程的理論是有差別的。上面描述的一致性和 ACID 事務中的一致性是兩回事。事務中的一致性包含了實際工程對狀態的后續處理。但是 CAP 定理并不涉及到狀態的后續處理,對于這些問題,后續出現了 BASE 理論等工程結論去處理,目前,只需要明白 CAP 定理主要描述的是狀態。
2.2. A:可用性
奧維德曾經說過:“行動被人們遺忘,結果卻將永存”。
這句話說明了結果的重要性,而可用性在 CAP 里就是對結果的要求。它要求系統內的節點們接收到了無論是寫請求還是讀請求,都要能處理并給回響應結果。只是它有兩點必須滿足的條件:
條件 1:返回結果必須在合理的時間以內,這個合理的時間是根據業務來定的。業務說必須 100 毫秒內返回,合理的時間就是 100 毫秒,需要 1 秒內返回,那就是 1 秒,如果業務定的 100 毫秒,結果卻在 1 秒才返回,那么這個系統就不滿足可用性。
條件 2:需要系統內能正常接收請求的所有節點都返回結果。這包含了兩重含義:
-
如果節點不能正常接收請求了,比如宕機了,系統崩潰了,而其他節點依然能正常接收請求,那么,我們說系統依然是可用的,也就是說,部分宕機沒事兒,不影響可用性指標。
-
如果節點能正常接收請求,但是發現節點內部數據有問題,那么也必須返回結果,哪怕返回的結果是有問題的。比如,系統有兩個節點,其中有一個節點數據是三天前的,另一個節點是兩分鐘前的,如果,一個讀請求跑到了包含了三天前數據的那個節點上,抱歉,這個節點不能拒絕,必須返回這個三天前的數據,即使它可能不太合理。
2.3. P:分區容忍性
分布式的存儲系統會有很多的節點,這些節點都是通過網絡進行通信。而網絡是不可靠的,當節點和節點之間的通信出現了問題,此時,就稱當前的分布式存儲系統出現了分區。但是,值得一提的是,分區并不一定是由網絡故障引起的,也可能是因為機器故障。
比如,我們的分布式存儲系統有 A、B 兩個節點。那么,當 A、B 之間由于可能路由器、交換機等底層網絡設備出現了故障,A 和 B 通信出現了問題,但是 A、B 依然都在運行,都在對外提供服務。這時候,就說 A 和 B 發生了分區。
還有一種情況也會發生分區,當 A 出現了宕機,A 和 B 節點之間通信也是出現了問題,那么我們也稱 A 和 B 發生了分區。
綜上,我們可以知道,只要在分布式系統中,節點通信出現了問題,那么就出現了分區。
那么,分區容忍性是指什么? 它是說,如果出現了分區問題,我們的分布式存儲系統還需要繼續運行。不能因為出現了分區問題,整個分布式節點全部就熄火了,罷工了,不做事情了。
3. CAP 怎么選擇
我們上面已經知道了,在設計分布式系統時,架構師們在 C、A、P 這三種特性里,只能選擇兩種。
但是,這道 CAP 的選擇題,就像別人在問你“小明的父親有三個孩子,老大叫大朗,老二叫二郎,請問老三叫什么”一樣。在以分布式存系統為限定條件的 CAP 世界里,P 是早已經確定的答案,P 是必須的。
因為,在分布式系統內,P 是必然的發生的,不選 P,一旦發生分區錯誤,整個分布式系統就完全無法使用了,這是不符合實際需要的。所以,對于分布式系統,我們只能能考慮當發生分區錯誤時,如何選擇一致性和可用性。
而根據一致性和可用性的選擇不同,開源的分布式系統往往又被分為 CP 系統和 AP 系統。
當一套系統在發生分區故障后,客戶端的任何請求都被卡死或者超時,但是,系統的每個節點總是會返回一致的數據,則這套系統就是 CP 系統,經典的比如 Zookeeper。
如果一套系統發生分區故障后,客戶端依然可以訪問系統,但是獲取的數據有的是新的數據,有的還是老數據,那么這套系統就是 AP 系統,經典的比如 Eureka。
說了這么多,其實 CAP 定理本質很簡單,它就是一種分布式系統設計的不同理念概括,包括它說的一致性,可用性和分區容錯性。這就類似一個大學的校訓,是極度概念化的東西。
所以,大白話來形容下 CAP 吧,CAP 就是告訴程序員們當分布式系統出現內部問題了,你要做兩種選擇:
- 要么遷就外部服務,像外包公司。
- 要么讓外部服務遷就你,像銀行。
遷就外部服務就是我們不能因為我們自己的問題讓外部服務的業務運行受到影響,所以要優先可用性。而讓外部服務遷就我們,就要優先一致性。
4. 對 CAP 的常見誤解
誤解一:分布式系統因為 CAP 定理放棄了 C 或者 A 中的其中一個
很多人在沒有對 CAP 做深入了解的情況下,聽到很多人說分布式系統必須在 CAP 三個特性里選擇兩個,就覺得一套分布式系統肯定要么只有可用性要么只有一致性,不存在完整的可用性和一致性功能。
這種理解是大有問題的。因為,P 這種問題發生的概率非常低,所以:
當沒有出現分區問題的時候,系統就應該有完美的數據一致性和可用性。
你什么時候見過一個系統,當內部沒有問題的時候,會經常讓外部請求卡一下的?要么就冷不丁的提供陳舊的老數據?那還能叫系統嗎?
誤解二:C 和 A 之間的選擇是針對整個分布式系統的,只能整體考慮 C 和 A 之間的選擇
這個理解也是不對的。當分區發生的時候,其實對一致性和可用性的抉擇是局部性的,而不是針對整個系統的。
可能是在一些子系統做一些抉擇,甚至很可能只需要對某個事件或者數據,做一致性和可用性的抉擇而已。
比如,當我們做一套支付系統的時候,會員的財務相關像賬戶余額,賬務流水是必須強一致性的。這時候,你就要考慮選 C。但是,會員的名字,會員的支付設置就不必考慮強一致性,可以選擇可用性 A。
一套分布式系統的運行,就像人生一樣,就是一次又一次的選擇。在不同階段,不同的時刻有不同的事件發生的時候,又怎么可能會有完全一樣的選擇呢?
誤解三:CAP 的三個特性只有是和否兩種極端選擇,而不是一個范圍
這種二元性的理解更是極其誤導人。
CAP 理論的三種特性不是 Boolean 類型的,不是一致和不一致,可用和不可用,分區和沒分區的這類二選一的選項。而是這三種特性都是范圍類型。
拿可用性來說,就像我從銀行取錢。當我目的是派發壓歲錢的時候,我很可能就想全要新票子,但是,新票子很可能就還得多一個步驟,就是需要拿舊票子去換一些新票,此時,我可以多等會兒,能拿到新票子就好。而當我的目的就是做生活花銷的時候,票子是新是舊,我根本不那么關心,快點拿到錢就行。這就是可用性的范圍需求之一,對時延性的要求。
再比如,分區容錯則由于探測機制的問題,可能還得各節點搞投票去協商分區是否存在,當某一臺機器出現了問題,可能不影響業務的話,就會被機器投票認為分區不存在。然后一直等到多數機器出現了問題,才會投票確認出現了分區問題。這就好像新冠疫情,還會分低、中、高風險區呢,不是一出現通信故障就都被邏輯認定為分區問題。
5. CAP 理論的一些疑問
疑問一:在遵從 CAP 定理的系統中是否適合任意的寫請求
首先,在 CAP 定理中,關于一致性會有多種說法,但是總的來說,都是在描述數據最新版本的可見性。而這些可見性往往代表的是讀請求返回的數據的可見性。
那么問題來了,當我們要求讀數據的可見性的時候,對寫數據有什么要求嗎?
比如,我們系統有三個節點,一個客戶端給這個系統發了一個寫請求,要求系統寫入一個值為 20 的數據。那么,如果要滿足 CAP 定理中的一致性,就需要在寫完 20 這個數據之后,當其他客戶端請求讀取這個值為 20 的數據之后,無論請求被轉發到系統中任何節點都能返回這個值。
這就要求寫入這個值為 20 的寫請求必須成功寫到三個節點上,此時,系統就滿足了寫一致性的。所以,我們可以說對于讀一致性的要求是同時約束了寫一致性的。
其次,在 CAP 定理中,可用性本身要求對讀、寫請求都要處理。如果我們以可用性作為標準的時候,在發生分區錯誤時,由于我們對讀請求并沒有強行要求返回完全準確的數據,所以,可能在本次讀請求之前的最近一次寫請求可能是部分失敗的。
同樣的例子,我們的分布式系統由三個節點組成,最近一次寫請求想把值為 20 的數據寫到三個節點上。但是,由于發生了分區問題,有一個節點通信故障,寫請求寫不過去,因此只有兩個節點包含了值為 20 的數據。
此時,寫請求會返回給客戶端一個結果,可能會告訴客戶端寫入成功了,也可能告訴客戶端寫入部分成功。
這時候,當后續的讀請求恰巧被發送到有通信故障的那個節點,系統可能只能返回一個空的結果。但是,由于系統處理和返回了讀寫請求,所以,系統是滿足了 CAP 中的可用性的。
疑問二:數據分片和數據副本的分布式系統是否都遵守 CAP 定理
我們知道,在一套大規模的分布式系統里,一定是既需要把海量數據做切分,存儲到不同的機器上,也需要對這些存儲了數據的機器做副本備份的。
那么,如果,一個分布式系統里只有數據分片存儲或者只有數據副本存儲,他們都會遵守 CAP 定理嗎?
答案是當數據分片時,也是要遵守 CAP 定理,但是,是種非常特殊的遵守。
當在一套分布式系統只有分片存儲的時候,CAP 理論會表現成什么樣?
比如,我們有個分布式系統,由三個節點 a、b、c 組成。其中節點 a 存放了 A 表的數據,b 存放了 B 表的數據,c 存放了 C 表的數據。
如果有一個業務,它的意圖是想往 A 表插入一條新數據,在 B 表刪除一條已有數據,在 C 表更新一條老數據,這個分布式系統該怎么處理這種業務?
技術上我們對這種一個意圖想做多件事的情況往往會包裝成一個事務。當我們包裝成一個事務以后,我們可能會通過先在 a 節點執行,然后去 b 節點執行,最后去 c 節點執行,等到都成功了,才會返回成功。
但是,發生了分區以后怎么辦?當在 a、b 節點都成功了,到 c 發現發生了通信故障?
此時,根據 CAP 定理,你有兩個選擇,要么就直接返回一個部分成功的結果給客戶端,要么直接卡死等客戶端超時或者返回失敗給客戶端。當返回部分成功的時候,這就是選擇了可用性(A),當卡死或者返回失敗給客戶端的時候,就是選擇了一致性(C)。
可是,我們將請求包裝成了事務,而事務是要求要么都成功,要么都失敗……為了遵守這種要求,對于分布式只有分片的情況,迫于客觀條件,只能選擇C。所以分片的分布式系統,往往都是 CP 的系統。
可選擇,但是無法選擇是分布式系統只有分片數據存儲的情況時,遵守 CAP 定理的特殊表現。
而當分布式系統是多個節點,每個節點存儲了完整的一套數據,別的節點只是完整數據的備份的時候,即使事務只在一臺機器上成功,當發生分區故障的時候,我們也是可以有充分的余地選擇是單機事務的回退 or 就此認為寫成功的。
單機事務的回退,就可以對外表現為選擇了一致性。
就此認為寫成功,則可以認為選擇了可用性。
疑問三:為何有時候區分一個系統是 AP 還是 CP 是如此之難
因為,就像我們前面講過的,由于 AP 或者 CP 的選擇,可能僅局限為整套系統的局部,甚至某些特殊的數據上,而我們又是用這種局部的特性去描述了整套系統,所以就導致了區分的困難。而這本身其實也日漸成為了 CAP 的一個大問題,從而被人詬病。
6. CAP 的不足
-
CAP 定理本身是沒有考慮網絡延遲的問題的,它認為一致性是立即生效的,但是,要保持一致性,是需要時間成本的,這就導致往往分布式系統多選擇 AP 方式
-
由于時代的演變,CAP 定理在針對所有分布式系統的時候,出現了一些力不從心的情況,導致很多時候它自己會把以前很嚴謹的數學定義改成了比較松弛的業務定義,類似于我們看到,CAP 定理把一致性、可用性、分區容錯都變成了一個范圍屬性,而這和 CAP 定理本身這種數學定理般的稱呼是有沖突的,出現了不符合數學嚴謹定義的問題。
-
在實踐中以及后來 CAP 定理的提出者也承認,一致性和可用性并不僅僅是二選一的問題,只是一些重要性的區別,當強調一致性的時候,并不表示可用性是完全不可用的狀態。比如,Zookeeper 只是在 master 出現問題的時候,才可能出現幾十秒的不可用狀態,而別的時候,都會以各種方式保證系統的可用性。而強調可用性的時候,也往往會采用一些技術手段,去保證數據最終是一致的。CAP 定理并沒有給出這些情況的具體描述。
-
CAP 理論從工程角度來看只是一種狀態的描述,它告訴大家當有錯的時候,分布式系統可能處在什么狀態。但是,狀態是可能變化的。狀態間如何轉換,如何修補,如何恢復是沒有提供方向的。
7. 引申出來的 BASE
正因為 CAP 以上的種種不足,epay 的架構師 Dan Pritchett 根據他自身在大規模分布式系統的實踐經驗,總結出了 BASE 理論。BASE 理論是對 CAP 理論的延伸,核心思想是即使無法做到強一致性(Strong Consistency),但應用可以采用適合的方式達到最終一致性(Eventual Consitency)。
BASE 理論是實踐工程的理論,它彌補了CAP 理論過于抽象的問題,也同時解決了 AP 系統的總體工程實踐思想,是分布式系統的核心理論之一,我們將在下一篇文章里,詳細的講解此套理論。
8. 大廠面試題
在文章最后,來幾道大廠關于 CAP 的面試真題,檢驗一下你的學習效果,hiahiahia
-
什么是 CAP 理論?
-
CAP 中的 P 是什么意思?
-
為什么說分布式系統,只能在 C、A 中二選一?
-
結合實際應用,CP、AP 該怎么選擇?
資料分享
這是我從某優質機構弄來的一些資料,內容我認為確實稱得上優質二字,如需領取,請點贊這篇文章,關注我然后點擊這里即可免費領取
首先分享一份學習大綱,內容較多,涵蓋了互聯網行業所有的流行以及核心技術,以截圖形式分享:
(億級流量性能調優實戰+一線大廠分布式實戰+架構師筑基必備技能+設計思想開源框架解讀+性能直線提升架構技術+高效存儲讓項目性能起飛+分布式擴展到微服務架構…實在是太多了)
其次分享一些技術知識,以截圖形式分享一部分:
Tomcat架構解析:
算法訓練+高分寶典:
Spring Cloud+Docker微服務實戰:
最后分享一波面試資料:
切莫死記硬背,小心面試官直接讓你出門右拐
1000道互聯網Java面試題:
Java高級架構面試知識整理:
[外鏈圖片轉存中…(img-usGqwic6-1625414623446)]
Spring Cloud+Docker微服務實戰:
[外鏈圖片轉存中…(img-Nay6GB6l-1625414623447)]
最后分享一波面試資料:
切莫死記硬背,小心面試官直接讓你出門右拐
1000道互聯網Java面試題:
[外鏈圖片轉存中…(img-c26Kf019-1625414623447)]
Java高級架構面試知識整理:
[外鏈圖片轉存中…(img-z7ETdpgX-1625414623448)]