2021-2-16:請問你知道分散式設計模式中的Quorum思想麼?

乾貨滿滿張雜湊發表於2021-02-16

有效個數(Quorum)

有效個數(Quorum)這個設計模式一般是指分散式系統的每一次修改都要在大多數例項上通過來確定修改通過。

問題背景

在一個分散式儲存系統中,使用者請求會發到一個例項上。通常在一個例項上面執行的修改,需要複製到其他的例項上,這樣可以保證在原例項掛了的情況下,使用者依然可以看到這個修改。這就涉及到一個問題,究竟複製到多少個其他例項上之後,使用者請求才會返回成功呢?如果複製的例項個數過多,那麼請求響應時間就會更長;如果複製的例項過少,則這個修改可能會丟失。取得這個平衡性很重要,這也是分散式 PACELC 中的 L(Latency) 與 C(Consistency) 的取捨。

解決方案

當一個修改,被叢集中的大部分節點(假設個數為N)通過之後,這個修改也就被這個叢集所接受。這個 N 就是有效個數。假設叢集數量為 n,那麼 N = n/2 + 1.例如 n = 5,則 N = 3.

這個有效個數,間接地體現了叢集中最多可以有多少個例項掛掉,這個數量就是 f = n - N。通常的,如果我們期望可以忍受 f 個例項掛掉,那麼叢集就至少要有 2f + 1 個例項。

以下就是兩個經典需要有效個數這個設計模式的場景:

  • 更新儲存叢集中的資料。同時還會涉及到最高水位線(High-Water Mark)這個設計模式,用於標註截止到哪裡的日誌,已經同步到了叢集中的大多數例項。
  • 選主。在主從(Leader and)設計模式中,被有效個數選舉為主的就是最終的主。

如何設計叢集個數

目前主流的叢集設計模式有如下兩種:

image

  • 第一種是主從同步模式:
    • 一種是請求發往主,主負責同步到其他從上面,之後返回。如果請求發到了從上面,則從發到主上面處理。例如 Zookeeper 就是這麼做的。
    • 另一種是,請求發到哪個例項,哪個例項就是主,主將請求同步到從上面。例如 Eureka 就是這麼設計的。
  • 第二種是分割槽模式,叢集中不同節點儲存不同資料。一般的,這個資料切分經常採用一致性雜湊。假設請求傳送到了 A,經過 A 的計算,這個資料需要儲存在 D,並且我們配置的儲存備份是一份,這個備份是在 E 上面,這樣這個請求就會被同步到 D,E 上面。ElasticSearch,Riak,Dynamo 就是這種類似的設計。

在這種設計模式下的系統,主要考慮兩點:

  • 寫操作的吞吐量。因為每次寫入叢集,都要複製到多個例項,所以肯定會對效能有所影響。一般的,複製是併發複製的,這個效能主要受本次同步最慢的那個例項的影響。
  • 可以容忍的例項掛掉的個數。這樣的叢集,如果我們期望可以忍受 f 個例項掛掉,那麼叢集就至少要有 2f + 1 個例項。

實現舉例

1. Zookeeper 的兩階段提交 + 半數以上寫入機制

客戶端把寫請求傳送到 leader 節點上(如果傳送的是 follower 節點,follower節點會把寫請求轉發到leader節點),leader節點會把資料通過proposal請求傳送到所有節點(包括自己),所有到節點接受到資料以後都會寫到自己到本地磁碟上面,寫好了以後會傳送一個ack請求給leader,leader只要接受到過半的節點傳送ack響應回來,就會傳送commit訊息給各個節點,各個節點就會把訊息放入到記憶體中(放記憶體是為了保證高效能),該訊息就會使用者可見了。

2. Riak,DynamoDB

預設情況下,是 P+A 以及 E+L 的系統,但是可以根據配置修改,主要基於NWR模型與同步和非同步備份。N 代表 N 個備份,W 代表要寫入至少 W 份才認為成功,R 表示至少讀取 R 個備份。配置的時候要求 W+R > N。 因為 W+R > N, 所以 R > N-W。這個是什麼意思呢?就是讀取的份數一定要比總備份數減去確保寫成功的倍數的差值要大。
也就是說,每次讀取,都至少讀取到一個最新的版本。從而不會讀到一份舊資料。當我們需要高可寫的環境的時候(例如,amazon 的購物車的新增請求應該是永遠不被拒絕的)我們可以配置W = 1 如果N=3 那麼R = 3。 這個時候只要寫任何節點成功就認為成功,但是讀的時候必須從所有的節點都讀出資料。如果我們要求讀的高效率,我們可以配置 W=N R=1。這個時候任何一個節點讀成功就認為成功,但是寫的時候必須寫所有三個節點成功才認為成功。
大家注意,一個操作的耗時是幾個並行操作中最慢一個的耗時。比如R=3的時候,實際上是向三個節點同時發了讀請求,要三個節點都返回結果才能認為成功。假設某個節點的響應很慢,它就會嚴重拖累一個讀操作的響應速度

3. MongoDB

MongoDB 和上面的 Dynamo 類似,MongoDB關於一致性、可用性的權衡,取決於三者:

  • write-concern: 表示當寫請求在value個MongoDB例項處理之後才向客戶端返回
  • read-concern: 設定是否必須從 primary 讀取最新的資料還是可以從 secondary 讀取最終一致性的資料。
  • read-preference: 對於replica set,是返回當前節點的最新資料,還是返回寫入節點最多的資料,還是根據一些函式計算出的資料。

微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer:

image

相關文章