面經梳理-分散式

fattree發表於2024-07-10

題目

請簡述一下CAP理論,我們常見的中介軟體分別側重點是什麼?簡述一下BASE理論?什麼是強一致性,弱一致性,最終一致性

CAP理論

CAP原則又稱CAP定理,指的是在一個分散式系統中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分割槽容錯性)這三個基本需求,最多隻能同時滿足其中的2個。

一致性 :資料在多個副本之間能夠保持一致的特性。
可用性:系統提供的服務一直處於可用的狀態,每次請求都能獲得正確的響應。
分割槽容錯性:分散式系統在遇到任何網路分割槽故障的時候,仍然能夠對外提供滿足一致性和可用性的服務。

分散式系統是避免不了分割槽的,分割槽容錯性是一定要滿足的。
假如現在有兩個分割槽N1和N2,N1和N2分別有不同的分割槽儲存D1和D2,以及不同的服務S1和S2。
在滿足一致性 的時候,N1和N2的資料要求值一樣的,D1=D2。
在滿足可用性的時候,無論訪問N1還是N2,都能獲取及時的響應。

現在有這樣的場景:
使用者訪問了N1,修改了D1的資料。
使用者再次訪問,請求落在了N2。此時D1和D2的資料不一致。

接下來:
保證一致性:此時D1和D2資料不一致,要保證一致性就不能返回不一致的資料,可用性無法保證。
保證可用性:立即響應,可用性得到了保證,但是此時響應的資料和D1不一致,一致性無法保證。
所以,可以看出,分割槽容錯的前提下,一致性和可用性是矛盾的。

CAP三者不可同得,那麼必須得做一些權衡。

CA without P❌

如果不要求P(不允許分割槽),則C(強一致性)和A(可用性)是可以保證的。但是對於分散式系統,分割槽是客觀存在的,其實分散式系統理論上是不可選CA的。

CP without A

如果不要求A(可用),相當於每個請求都需要在Server之間強一致,而P(分割槽)會導致同步時間無限延長,如此CP也是可以保證的。很多傳統的資料庫分散式事務都屬於這種模式。

AP wihtout C

要高可用並允許分割槽,則需放棄一致性。一旦分割槽發生,節點之間可能會失去聯絡,為了高可用,每個節點只能用本地資料提供服務,而這樣會導致全域性資料的不一致性。現在眾多的NoSQL都屬於此類。

redis:單機AC,主從、哨兵、叢集AP
es:個人認為AP
kafka:個人認為AP
consul:CP,保證一致性的前提下,儘量保證可用性

BASE理論

BASE 是 Basically Available(基本可用) 、Soft-state(軟狀態) 和 Eventually Consistent(最終一致性) 三個短語的縮寫。

BASE 理論是對 CAP 中一致性 C 和可用性 A 權衡的結果,其來源於對大規模網際網路系統分散式實踐的總結,是基於 CAP 定理逐步演化而來的,它大大降低了我們對系統的要求。

BASE理論的核心思想是:

即使無法做到強一致性(Strong consistency),但每個應用都可以根據自身的業務特點,採用適當的方式來使系統達到最終一致性(Eventual consistency)。

  • 基本可用

什麼是基本可用呢?

假如系統出現了不可預知故障,允許損失部分可用性,當然也不能完全不可用。

損失的這部分可用性指的是什麼?

響應時間上的損失:正常情況下的搜尋引擎0.5秒即返回給使用者結果,而基本可用的搜尋引擎可以在2秒作用返回結果。

功能上的損失:在一個電商網站上,正常情況下,使用者可以順利完成每一筆訂單。但是到了大促期間,為了保護購物系統的穩定性,部分消費者可能會被引導到一個降級頁面。

  • 軟狀態

軟狀態指允許系統中的資料存在中間狀態(CAP 理論中的資料不一致),並認為該中間狀態的存在不會影響系統的整體可用性,即允許系統在不同節點的資料副本之間進行資料同步的過程存在延時。

  • 最終一致性

最終一致性強調的是系統中所有的資料副本,在經過一段時間的同步後,最終能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終資料能夠達到一致,而不需要實時保證系統資料的強一致性。

分散式一致性的 3 種級別:

強一致性 :系統寫入了什麼,讀出來的就是什麼。
弱一致性 :不一定可以讀取到最新寫入的值,也不保證多少時間之後讀取到的資料是最新的,只是會儘量保證某個時刻達到資料一致的狀態。
最終一致性 :弱一致性的升級版,系統會保證在一定時間內達到資料一致的狀態。
業界比較推崇是最終一致性級別,但是某些對資料一致要求十分嚴格的場景比如銀行轉賬還是要保證強一致性。

最終一致性怎麼保證呢?

讀時修復 : 在讀取資料時,檢測資料的不一致,進行修復。比如 Cassandra 的 Read Repair 實現,具體來說,在向 Cassandra 系統查詢資料的時候,如果檢測到不同節點 的副本資料不一致,系統就自動修復資料。
寫時修復 : 在寫入資料,檢測資料的不一致時,進行修復。比如 Cassandra 的 Hinted Handoff 實現。具體來說,Cassandra 叢集的節點之間遠端寫資料的時候,如果寫失敗 就將資料快取下來,然後定時重傳,修復資料的不一致性。
非同步修復 : 這個是最常用的方式,透過定時對賬檢測副本資料的一致性,並修復。

小結

CAP 是分散式系統設計理論,BASE 是 CAP 理論中 AP 方案的延伸,ACID 是資料庫事務完整性的理論。

CAP理論嚴格來講不是三選二,而是CP、AP二選一,因為通常P(分割槽容錯性)是必須得到保證的。

BASE理論面向的是大型高可用、可擴充套件的分散式系統。與傳統ACID特性相反,不是強一致性模型,BASE提出透過犧牲強一致性來獲得可用性,並允許資料一段時間內的不一致,但是最終需要達到一致狀態。

參考:
分散式必備理論基礎:CAP和BASE

有了解過哪些一致性協議?Poxos、ZAB、raft協議有了解?有了解過gossip協議?

一致性協議

顧名思義,一致性協議的應用是為了維護各節點資料的一致性,換句話說,就是使叢集裡的節點儲存的資料是一模一樣的。那麼這樣的技術,在現實生活中真的迫切需要嘛?其實這樣的需求從古至今一直存在,這裡我們舉個趣味性的例子,可能很多人知道拜占庭將軍問題,古時候東羅馬帝國幅員遼闊,各個將軍掌管各自的軍隊分散在帝國各處,他們之間靠信差交流。當東羅馬帝國遭遇戰爭,將軍們需要聯合起來才能打倒敵方軍隊。這個時候將軍們需要對最基本的進攻還是撤退達成一致。如果一齊進攻,他們能打倒敵人,如果一齊撤退,他們還有機會東山再起。此時將軍們派出各自的信差去傳達指令。那麼帝國裡有那麼多將軍,大家如何達成一致?最簡單的,指派一個上將軍,大家都聽他的命令就好。那麼上將軍將怎麼安排自己的信使?如果信使路上遇到了危險不幸逝去,那麼長時間等不來回信的上將軍該怎麼處理?如果不幸逝世的是上將軍怎麼辦?如果上將軍是敵方間諜,專門傳遞虛假訊息怎麼辦?比如間諜上將軍對A將軍傳達撤退命令,對B將軍傳達進攻命令,那麼最終只有B將軍進攻,B就會全軍覆沒!這些情況其實都真實反映一致性協議需要考慮的種種問題。

一致性協議可以有多種分類方法,可以從單主和多主的角度對協議進行分類。單主協議,即整個分散式叢集中只存在一個主節點,採用這個思想的主要有2PC, Paxos, Raft等. 另一類是多主協議,即整個叢集中不只存在一個主節點,Pow協議以及著名的Gossip協議。

單主協議由一個主節點發出資料,傳輸給其餘從節點,能保證資料傳輸的有序性。而多主協議則是從多個主節點出發傳輸資料,傳輸順序具有隨機性,因而資料的有序性無法得到保證,只保證最終資料的一致性。這是單主協議和多主協議之間最大的區別。單主協議中具有代表性的為Paxos, Raft兩大協議,多主協議中具有代表性的為Gossip和Pow協議。

Gossip協議

Gossip又被稱為流行病演算法,它與流行病毒在人群中傳播的性質類似,由初始的幾個節點向周圍互相傳播,到後期的大規模互相傳播,最終達到一致性。Gossip協議被廣泛應用於P2P網路,同時一些分散式的資料庫,如Redis叢集的訊息同步使用的也是Gossip協議,另一個重大應用是被用於比特幣的交易資訊和區塊資訊的傳播。

Gossip協議的整體流程非常簡單,初始由幾個節點發起訊息,這幾個節點會將訊息的更新內容告訴自己周圍的節點,收到訊息的節點再將這些資訊告訴周圍的節點。依照這種方式,獲得訊息的節點會越來越多,總體訊息的傳輸規模會越來越大,訊息的傳偶速度也越來越快。雖然不是每個節點都能在相同的時間達成一致,但是最終叢集中所有節點的資訊必然是一致的。Gossip協議確保的是分散式叢集的最終一致性。

Proof-of-work(Pow)演算法與比特幣

Proof-of-work演算法又被稱為Pow演算法,其實從這個演算法的名稱中我們能對它實現的功能窺見一二,工作量證明演算法,那是否意味著工作量較大的某一個節點能夠獲得主動權呢?事實也是類似這個原理,大量的節點參與競爭,透過自身的工作量大小來證明自己的能力,最終能力最大的節點獲得優勝,其他節點的資訊需要與該節點統一。Pow最為人所熟知的應用是比特幣。下面就以比特幣為例具體講解該演算法。

我們知道,比特幣塑造的是一個去中心化的交易平臺,最重要的一點就是該平臺的可信度。要達到高可信度,要求整個系統中沒有固定的leader,且為了防止外界篡改,必然要設定一些特殊的機制,比如讓圖謀不軌的一方無法篡改或者必須付出與收穫完全不等稱的代價才有修改的可能,以這樣的方式打消其修改的念頭。這時候比特幣引入了Pow演算法,在Pow機制下,所有參與者共同求解數學問題,這些數學問題往往需要經過大量列舉才能求解,因此需要參與者消耗大量的硬體算力。成功求解數學問題的參與者將獲得記賬權,並獲得比特幣作為獎勵。其餘所有參與者需要保持和獲得記賬權節點的區塊一致,由此達到最終的一致性。

依靠Pow演算法,比特幣很大程度保證了交易平臺的安全性。因為如果要對該平臺的資料進行篡改或者毀壞,篡改者至少需要獲得比特幣全網一半以上的算力,這是非常難以達到的。但是同樣Pow存在很多缺點,Pow達成一致性的速度很慢,應用在比特幣中每秒鐘只能做成7筆交易,這在大部分的商業應用中都是達不到要求的。其次Pow造成了很大的資源浪費。所有的競爭者奪取記賬權需要付出巨大的硬體算力,這在背後是大量的硬體成本、電力損耗,而一旦記賬權確定,其餘沒有獲得記賬權的節點的算力等於白白浪費。最後是現在出現了一些大規模的專業礦場,這些礦場的算力非常強大,它們的存在增大了平臺被篡改的可能性。

在比特幣的應用中,使用Pow演算法確定競爭者的記賬權,儘可能地解決"拜占庭將軍"問題,再將可信的結果由傳播速度極強,節點數目量大的Gossip協議去進行傳輸,最終達成全網一致,可謂很好地利用這兩大演算法的特點,將二者優劣互補並巧妙地用於區塊鏈領域。

Paxos協議

有點繞,沒細看。

Paxos是非常經典的一致性協議,但是因為過於理論化,難以直接工程化,因此工業界出現了諸多基於Paxos思想出發的變種。雖然這些變種最終很多都和原始的Paxos有比較大的差距,甚至完全演變成了新的協議,但是作為奠基者的Paxos在分散式一致性協議中依然持有不可撼動的地位。

Raft協議

Raft的核心思想和Paxos是非常一致的,甚至可以說,Raft是基於Paxos的一種具體化實現和改進,它讓一致性演算法更容易為人所接受,更容易得到實現。

多個server進行選舉拉票,當且僅當某一個candidate收到超過一半的server的票數時,它就成功當選了leader,並開始向每個server傳送心跳資訊。那麼當選舉成功後,整個叢集進入log傳輸的狀態。

Zab

把節點分兩種,Leader(主)和Follower(從)。 有一個主節點,所有寫操作全部透過節點進行處理,如果一個從節點收到了一個寫操作請求,就會轉給主節點處理。 其他節點都是從節點,可以透過從節點進行讀操作。 主節點透過選舉得出,主節點失蹤後,其他從節點自動開始選舉新的主節點。應用於Zookeeper。

應用

比特幣:在比特幣的應用中,使用Pow演算法確定競爭者的記賬權,儘可能地解決"拜占庭將軍"問題,再將可信的結果由傳播速度極強,節點數目量大的Gossip協議去進行傳輸,最終達成全網一致,可謂很好地利用這兩大演算法的特點

redis:選主,Raft;訊息傳遞,Gossip

zookeeper:Zab 是特別為 Zookeeper 設計的支援崩潰恢復的原子廣播協議,在 Zookeeper 中主要依賴 Zab 協議實現資料一致性,基於該協議,Zookeeper 實現了一種主備模型(Leader 與 Follower)的系統架構保證叢集中各個副本之間的資料一致性。

參考:
分散式一致性協議概述
幾種常見的分散式一致性協議介紹
Zookeeper的Zab協議詳解

一致性hash演算法瞭解麼?

傳統hash演算法,在分散式儲存環境下,如果增加或者減少節點,大量key需要重新hash,導致大量key需要遷移

一致性雜湊是將整個雜湊值空間組織成一個虛擬的圓環,如假設雜湊函式H的值空間為0-2^32-1(雜湊值是32位無符號整形),將儲存節點和key都對映到hash環上的某個位置,一個儲存節點只需要管理一個區間的key,如果增加或者減少節點,則只要移動一小部分key,其他的key不用變。

當儲存節點太少的時候,可能出現各節點的儲存key不均衡的情況,所以後續出現了虛擬節點方案,在hash環上設定多個虛擬節點,這樣可以保證資料在各個虛擬節點上大致分佈均勻,然後將虛擬節點均勻的分配給各個實際的儲存節點上即可。redis叢集即採用了虛擬節點方案進行資料分配。

參考:
5分鐘理解一致性雜湊演算法

分散式鎖的實現方案?

redis、zookeeper、資料庫、consul、etcd都可以。

常用為redis,redis可以用setnx或者redisson裡的鎖,redisson裡還有redlock,實現分散式鎖的高可用

個人使用經驗:
加鎖:
先設定超時時間,迴圈呼叫String nx = jedisCluster.set(key, "value", "NX", "EX", seconds);直到返回“OK”,或者直到超時,key是鎖的標誌,value是加鎖執行緒設定的uuid,不可重入
解鎖:
基於lua指令碼實現原子性,如果有key,且value是原來設定的uuid,則清理key並返回“OK”,否則返回0

基於redis的setnx的問題在於follower複製leader可能存在延遲,當leader掛掉,follower頂替時,可能導致鎖丟失。替代方案可以考慮redlock或其他高可用方案。

參考:
Java分散式鎖看這篇就夠了
jedis setnx實現鎖機制

聊聊微服務治理,分別涉及到哪些方面?你們系統在高可靠上採取了哪些措施?應對高併發有什麼方案?系統的監控運維和服務降級、熔斷的方案是什麼?

可靠性與可用性

Reliability和Availability分別對應可靠性和可用性, 這兩個概念既有區別也有聯絡:

Reliability定義為一個服務連續無故障執行的時間,無故障執行的時間越長,可靠性就越高。
Availiability定義為在足夠長的時間裡,比如一年的時間裡,一個服務可用的時間,服務可用時間越長越好。一般用可服務時間除於總時間算出一個百分比,用百分比作為度量。比如一個服務如果有5個9的可用性,指的就是一年裡99.999%時間裡服務都是可用的。
有兩個極端的例子可以很好的說明這兩個概念的區別:

假想一個服務,可靠性很高,平均來說可以穩定執行10年,但是一旦服務中斷,要用一年的時間來恢復,那麼它的可用性只有90%。
假想另一個服務,可靠性很差,執行10秒就會當機,但是恢復服務只需要1ms, 那麼它的可用性是99.99%

微服務治理

服務註冊與發現。
可觀測性。包括 監控、日誌、呼叫追蹤,呼叫拓撲關係等。
安全。不同微服務承載自身獨有的業務職責,對於業務敏感的微服務,需要閘道器對其他服務的訪問進行認證與鑑權,也就是安全問題。
高可用。使用熔斷限流降級等方式,來應對服務不可用帶來的各種風險。

具體方案

熔斷降級,hystrix
限流,springcloud gateway 預設令牌桶演算法
監控,springboot+Prometheus+grafana監控。自研資料鏈路監控。
服務註冊,spring cloud consul
安全,閘道器鑑權filter,token認證,介面認證,

參考:
高可靠和高可用的區別
微服務與服務治理

如何做微服務的限流?常見的限流演算法有哪些?漏桶演算法和令牌桶演算法的區別是什麼?

springcloud gateway 預設令牌桶演算法

漏桶可以一次快取大量請求,但是打給後端的請求時勻速的;令牌桶可以快速把大量請求打給後臺,峰值更高,但是不會超過令牌桶的容量,效率高。

參考:
令牌桶和漏桶區別總結

聊聊你理解的resful框架,和rpc框架的區別?

RPC是以一種呼叫本地方法的思路來呼叫遠端方法,透過各種RPC框架隱藏呼叫遠端方法的細節,讓使用者以為呼叫的就是本地方法。RPC隱藏了底層網路通訊的複雜度,讓我們更專注於業務邏輯的開發。

REST透過HTTP實現,把使用者的需求抽象成對資源的操作,使用者必須透過HTTP協議的GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS七種基本操作去和伺服器互動。

RPC通常是伺服器元件之間的通訊,比如和中介軟體的通訊,MQ、分散式快取、分散式資料庫等等。

而REST通常是面向客戶端的(一般是瀏覽器),他們的使用場景也是不一樣的。

參考:
RPC和RESTful的區別

常見的RPC框架有哪些?能否做個對比?

多語言一般為gRPC、Thrift;java一般為Dubbo、Spring Cloud

GRPC主要就是搞了個ProtoBuf,然後採用HTTP協議,所以協議部分沒有重複造輪子,重點就在ProtoBuf上。
Thrift的資料格式是用的現成的,沒有單獨搞一套,但是它在傳輸層和服務端全部是自己造輪子,所以可以對協議層、傳輸層有多種控制要求。
Thrift效能較高,更加靈活,支援語言多,但是文件較少;GRPC文件例項詳細,比較節省網路頻寬,功能少但是實現較好。

Dubbo需要基於zk構建遠端呼叫,需要自己進行配置搭建;spring cloud則提供了完整的微服務生態體系,feign遠端呼叫只是其中的一部分,一般更加推薦直接使用springcloud。

參考:
RPC框架原理到選型:gRPC、Thrift、Dubbo、Spring Cloud

Grpc框架的原理有了解?

序列化基於protobuffer,網路基於http2協議
基於proto協議生成多種語言的程式碼,然後基於生成程式碼進行遠端呼叫。

參考:
玩轉gRPC—深入概念與原理

相關文章