分佈式存儲系統的數據分佈算法

前言

分佈式存儲系統 面臨着的首要問題,就是如何將 大量的數據 分佈在 不一樣的存儲節點 上。不管上層接口是 KV 存儲對象存儲塊存儲、亦或是 列存儲,在這個問題上大致是一致的。本文將介紹如何 分佈式存儲系統作數據分佈目標 及可選的 方案,並試着總結和權衡他們之間的關係及。算法

正文

(一) 指標

這裏假設 目標數據 是以 key 標識的 數據塊對象。在一個包含 多個存儲節點 的集羣中,數據分佈算法 須要爲每個給定的 key 指定 一個多個 對應的 存儲節點 負責,數據分佈算法 有兩個基本目標:負載均衡

  • 均勻性(Uniformity):不一樣存儲節點的 負載 應該 均衡
  • 穩定性(Consistency):每次一個 key 經過 數據分佈算法 獲得的 分佈結果 應該保持基本穩定,即便再有存儲節點發生變化的狀況下。

能夠看出,這兩個目標在必定程度上是 相互矛盾 的。當有 存儲節點增長或刪除 時,爲了保持穩定應該 儘可能少 的進行 數據的移動從新分配,而這樣又勢必會帶來 負載不均衡。一樣追求 極致均勻 也會致使較多的 數據遷移分佈式

因此咱們但願在這兩個極端之間,找到一個點以得到合適的均勻性和穩定性。除了上述兩個基本目標外,工程中還須要從如下幾個方面考慮數據分佈算法的優劣:性能

  1. 性能可擴展性:這個主要考慮的是算法相對於 存儲節點規模時間複雜度。爲了整個系統的可擴展性,數據分佈算法不該該在集羣規模擴大後顯著的增長運行時間。
  2. 考慮節點異構:實際工程中,不一樣 存儲節點 之間可能會有很大的 性能容量差別,好的數據分佈算法應該能很好的應對這種 異構,提供 加權的數據均勻
  3. 隔離故障域:爲了 數據的高可用,數據分佈算法應該爲每一個 key 找到 一組存儲節點,這些節點可能提供的是 數據的鏡像副本,也多是相似 擦除碼 的副本方式。數據分佈算法應該儘可能 隔離 這些副本的故障域,如 不一樣機房不一樣機架不一樣交換機不一樣機器

(二) 演進

看完算法的評價指標後,接下來介紹一些可能的方案演進,並分析他們的優劣。這裏假設 key 的值足夠分散。優化

1. Hash

一個簡單直觀的想法是直接用 Hash 來計算,簡單的以 Key 作 哈希對節點數取模。能夠看出,在 key 足夠分散的狀況下,均勻性 能夠得到,但一旦有 節點加入退出 時,全部的原有節點都會受到影響。穩定性 無從談起。3d

2. 一致性Hash


一致性 Hash 能夠很好的解決 穩定性問題,能夠將全部的 存儲節點 排列在收尾相接的 Hash 環上,每一個 key 在計算 Hash 後會 順時針 找到先遇到的 存儲節點 存放。而當有節點 加入退出 時,僅影響該節點在 Hash 環上 順時針相鄰後續節點。但這有帶來 均勻性 的問題,即便能夠將存儲節點等距排列,也會在 存儲節點個數 變化時帶來 數據的不均勻。而這種可能成倍數的不均勻 在實際工程中是不可接受的。orm

3. 帶負載上限的一致性Hash

一致性 Hash 有 節點變化時不均勻的問題。Google 在 2017 年提出了 Consistent Hashing with Bounded Loads 來控制這種 不均勻的程度。簡單的說,該算法給 Hash 環上的每一個節點一個 負載上限 爲 1 + e 倍的 平均負載,這個 e能夠自定義。當 key 在 Hash 環上 順時針找到合適的節點後,會判斷這個節點的 負載 是否已經 到達上限,若是 已達上限,則須要繼續找 以後的節點 進行分配。cdn


如上圖所示,假設每一個桶 當前上限 是 2,紅色的小球按序號訪問,當編號爲 6 的紅色小球到達時,發現順時針首先遇到的 B(3,4),C(1,5)都已經 達到上限,所以最終放置在桶 A 裏。對象

這個算法最吸引人的地方在於 當有節點變化 時,須要遷移的數據量是 1/e^2 相關,而與 節點數數據數量 均無關。blog

也就是說當 集羣規模擴大 時,數據遷移量 並不會隨着顯著增長。另外,使用者能夠經過調整 e 的值來控制 均勻性穩定性 之間的權衡,就是一種 以時間換空間 的算法。整體來講,不管是 一致性 Hash 仍是 帶負載限制一致性 Hash,都沒法解決 節點異構 的問題。

4. 帶虛擬節點的一致性Hash

爲了解決 負載不均勻異構 的問題,能夠在 一致性 Hash 的基礎上引入 虛擬節點。即 hash 環上的 每一個節點 並非 實際存儲節點,而是一個 虛擬節點。實際的 存儲節點 根據其 不一樣的權重,對應 一個多個虛擬節點,全部落到相應虛擬節點上的 key 都由該 存儲節點負責

以下圖所示,存儲節點 A 負責 (1,3],(4,8],(10, 14],存儲節點 B 負責 (14,1],(8,10]。


這個算法的問題在於,一個 實際存儲節點加入退出,會影響 多個虛擬節點的從新分配,進而引發 不少節點 參與到 數據遷移 中來。

另外,實踐中將一個 虛擬節點 從新分配給 新的實際節點 時,須要將這部分數據 遍歷 出來 發送給新節點。咱們須要一個更合適的 虛擬節點切分分配方式,那就是 分片

5. 分片

分片哈希環 切割爲 相同大小的分片,而後將這些 分片 交給 不一樣的節點 負責。

注意這裏跟上面提到的 虛擬節點 有着很 本質的區別分片的劃分和分片的分配被解耦

一個 節點退出 時,其所負責的 分片 並不須要 順時針合併 給以後節點,而是能夠更靈活的 將整個分片 做爲一個 總體 交給 任意節點。在實踐中,一個 分片 多做爲 最小的數據遷移備份單位


而也正是因爲上面提到的 解耦,至關於將原先的 key 到 節點映射 拆成了兩層。須要一個新的機制 來進行 分片存儲節點映射。因爲 分片數 相對 key 空間已經很小而且 數量肯定,能夠更精確地初始設置,並引入 中心目錄服務 來根據 節點存活 修改 分片的映射關係。同時將這個 映射信息 通知給全部的 存儲節點客戶端


上圖是 分佈式KV存儲 Zeppelin中的 分片方式,Key Space 經過 Hash 到 分片分片及其副本 又經過一層映射到 最終的存儲節點 Node Server。

6. CRUSH算法

CRUSH 算法本質上也是一種 基於分片 的數據分佈方式,其試圖在如下幾個方面進行優化:

  • 分片映射信息量:避免 中心目錄服務存儲節點客戶端之間 交互大量的 分片映射信息,而改由 存儲節點客戶端 本身根據 少許穩定 的集羣節點拓撲和肯定的規則本身計算分片映射。
  • 完善的故障域劃分:支持 層級故障域控制,將 同一分片不一樣副本 按照配置劃分到 不一樣層級故障域中

客戶端存儲節點 利用 key、存儲節點拓撲結構分配算法,獨立的進行 分片位置 的計算,獲得一組負責對應 分片副本存儲位置

如圖所示是 一次定位 的過程,最終選擇了一個 row 下的 cab21,cab23,cab24 三個機櫃下的三個存儲節點。


節點變化 時,因爲 節點拓撲 的變化,會影響 少許分片 數據進行遷移,以下圖是加入 新節點 引發的 數據遷移。經過良好的 分配算法,能夠獲得很好的 負載均衡穩定性,CRUSH 提供了 Uniform、List、Tree、Straw 四種分配算法。

(三) 應用案例

常見的 分佈式存儲系統 大多采用相似於 分片數據分佈和定位方式

  1. Cassandra/Dynamo:採用 分片 的方式並經過 Gossip 協議在對等節點間通訊;
  2. Redis Cluster:將 key Space 劃分爲 slots,一樣利用 Gossip 協議通訊;
  3. Zeppelin:將數據分片爲 Partition,經過 Meta 集羣提供 中心目錄服務
  4. Bigtable:將數據切割爲 Tablet,相似於可變的分片,Tablet Server 能夠進行分片的切割,最終分片信息記錄在 Chubby 中;
  5. Ceph:採用 CRUSH 方式,由 中心集羣 Monitor 提供並維護 集羣拓撲 的變化。
相關文章
相關標籤/搜索