1. 程式人生 > >2021-2-22:請你說下 CAP 理論並舉例

2021-2-22:請你說下 CAP 理論並舉例

# CAP CAP 理論是分散式系統中的一個老生常談的理論了,最早由 [Eric Brewer](https://www.linkedin.com/in/eric-brewer-1031254) 在[一個講座](https://people.eecs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf)中提出。在這個講座中,在傳統 ACID 理論以及當時比較流行但是比較抽象的的設計指導理論 BASE 理論(當時的 BASE 理論還很抽象,直到好幾年後才出現一份比較權威的被廣泛接受的 BASE 理論完整解釋和設計)的類比中,提出 - C(Consistency,一致性):在一個分散式的系統中,**同一個資料**的**所有備份**,在**同一時刻**是否有相同的值。也就是,對於同一個資料的讀寫,是否立刻對於所有副本都能看到一致的結果。一種比較常見的強一致性實現就是,在看到一致的結果之前,寫請求不返回,讀請求阻塞或者超時。 - A(Availability,可用性):在叢集中一些節點故障時,叢集還可以**響應讀寫請求**。 - P(Partition-tolerance,分割槽容忍性):分散式系統具有多個節點,如果節點間網路中斷,就會造成**分割槽**。 並且提出了,CAP **並不能全部滿足**,而是**一般選兩個滿足** 之後,[Seth Gilbert](https://www.comp.nus.edu.sg/~gilbert/) 以及 [Nancy Lynch](https://www.csail.mit.edu/person/nancy-lynch) 在[一篇 Notes ](https://users.ece.cmu.edu/~adrian/731-sp04/readings/GL-cap.pdf)中,證明了 **CAP 並不能同時都滿足**。並且,將 CAP 定義的更加清晰: - C: 需要滿足原子一致性,也就是任何讀寫都是具有原子性的,也就是對於同一個資料的寫之後的讀取,一定能讀取到寫的值,也就是**最新的值** - A:對於所有成功的請求,都需要在有限的時間內返回,也就是成功請求是有效的,可終止的。 - P:可能**節點間傳輸丟失一些訊息**。 ## CA 系統 也就是不允許分割槽的系統,也其實就不是分散式系統,而是單機系統。例如單機資料庫,或者是共享儲存資料庫,比如 Aurora DB 類似的思路設計的資料庫,共享同一份儲存,上面建立不同的 MySQL 程序,一個 MySQL 讀寫,其他的只讀,由於使用的同一塊儲存,並且只有一個 MySQL 程序寫入,滿足 ACID 的事務特性,能保證強一致性,以及可用性。 ## CP 系統 也就是**不要求高可用性,但是要求強一致性的系統**,哪怕當前業務不可用,也不能出現數據不一致的情況。並且,如果節點間傳輸訊息丟失導致沒有同步成功,或者**重試**,或者返回**更新失敗,回滾更新請求**。 CP 的一種實際應用就是**分散式鎖**,一般的,如果沒有獲取到鎖,或者獲取鎖失敗,我們都會選擇阻塞等待,或者直接失敗,而不會冒著可能會有併發危險而去執行業務。並且,分散式鎖必須保持所有節點看到的鎖狀態一致,不能有差異,否則認為獲取鎖失敗。 同時,大部分分散式資料庫都是 CP 系統,但是他們的一致性協議方案是不同的,常見的例如 Paxos,2PC,3PC,RAFT等等。 ## AP 系統 也就是**要求高可用性,但是不用強一致性的系統**。在這種情況下,一旦分割槽發生,節點間的資料可能不一致,每個節點用自己的本地資料繼續提供服務。這樣情況下,可能會出現資料不一致,系統一般會實現**最終一致性**。也就是在分割槽結束後,通過一些機制將資料同步。 基本上具有**多層快取的系統**,都是 AP 的系統設計。例如 DNS,客戶端快取,瀏覽器快取以及程序內快取等等。 ## 一個 CP 與 AP 系統的對比 一個比較經典的例子就是 Zookeeper 作為註冊中心和 Eureka 作為註冊中心。 假設註冊中心有兩個介面,一個是註冊例項,一個是讀取例項。 如果以 Zookeeper 為註冊中心,對於註冊例項請求也就是更新請求,採用的是過半寫以及 2 PC 的同步機制。 ![image](https://zhxhash-blog.oss-cn-beijing.aliyuncs.com/%E5%85%A8%E7%BD%91%E6%9C%80%E7%A1%AC%E6%A0%B8%E8%A7%A3%E6%9E%90%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83-Zookeeper.png) 只有過半 2PC 更新成功,這個註冊請求才成功,這樣讀取每個節點都會讀取到這個更新請求,否則會回滾已經更新的節點。並且每個節點資料是一致的。如果過半的節點不可用,那麼整個叢集都不能處理註冊例項請求以及讀取例項的請求。這樣保證的強一致性,但是可用性是打了折扣的。 如果以 Eureka 為註冊中心,註冊請求發到一個 Eureka 例項上之後,這個 Eureka 會轉發到叢集內其他 Eureka 節點。 ![image](https://zhxhash-blog.oss-cn-beijing.aliyuncs.com/%E5%85%A8%E7%BD%91%E6%9C%80%E7%A1%AC%E6%A0%B8%E8%A7%A3%E6%9E%90%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83-Eureka.png) 即使某些節點失敗,也不會將已經更新的回滾。並且無論叢集內哪些 Eureka 掛了,也不會影響其他正常的 Eureka 繼續服務工作,雖然可能讀取到比較老的資料,以及有一些資料不一致。 ## 目前的 CAP 理論 隨著技術的不斷髮展以及理論的不斷完善,我們發現,分割槽並不是會經常出現的情況,大部分情況下,如果我們忽略 P ,其實就是可以實現 CA 共存的情況。如果分割槽是可以感知的,納悶我們可以提前制定響應策略,例如進入服務降級限制某些操作,通過恢復補償邏輯修正資料不一致。 在 CAP 基礎上演變的 PACELC 理論,就是針對這種情況的更為實際的指導意見。在出現分割槽的情況下,取前半部分,其實還是 CAP 理論。如果不出現分割槽的情況,也就是大部分的情況下,我們考慮 L(Latency,延遲) 與 C(Consistency 一致性)的權衡。 > **微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer**: >![image](https://zhxhash-blog.oss-cn-beijing.aliyuncs.com/qr-c