一致性 Hash 原理與實現

Redis 集羣的使用

在使用 Redis 的過程中,爲了保證 Redis 的高可用,我們一般會對 Redis 做主從複製,組成Master-Master 或者 Master-Slave的形式,進行數據的讀寫分離。

Master-Slave模式
舉個例子,我們有一個電商平臺,需要使用 Redis 存儲商品的圖片資源,存儲的格式爲鍵值對, key 值爲圖片名稱,value 爲該圖片所在的文件服務器的路徑,我們需要根據文件名,查找文件所在的文件服務器上的路徑,我們的圖片數量大概在 3000w 左右,按照我們的規則進行分庫,規則就是隨機分配的,我們以每臺服務器存 500w 的數量,部署 12 臺緩存服務器,並且進行主從複製,架構圖如下圖所示:

Redis分庫分表
由於我們定義的規則是隨機的,所以我們的數據有可能存儲在任何一組 Redis 中,比如我們需要查詢“produc.png”的圖片,由於規則的隨機性,我們需要遍歷所有 Redis 服務器,才能查詢得到。這樣的結果顯然不是我們所需要的。所以我們會想到按某一個字段值進行 Hash 值、取模。所以我們就看看使用 Hash 的方式是怎麼進行的。

使用 Hash 的 Redis 集羣

如果我們使用 Hash 的方式,每一張圖片在進行分庫的時候都可以定位到特定的服務器,示意圖如圖所示:

使用Hash方式的命中緩存
從上圖中,我們需要查詢的是圖product.png,由於我們有 6 臺主服務器,所以計算的公式爲:hash(produc.png) % 6 = 5,我們就可以定位到是 5 號主從,這樣就省去了遍歷所有服務器的時間,從而大大提升了性能。

使用 Hash 時遇到的問題

在上述 hash 取模的過程中,我們雖然不需要對所有 Redis 服務器進行遍歷而提升了性能。但是,使用 Hash 算法緩存時會出現一些問題,Redis 服務器變動時,所有緩存的位置都會發生改變

比如,現在我們的 Redis 緩存服務器增加到了 8 臺,我們計算的公式從 hash(product.png) % 6 = 5 變成了 hash(product.png) % 8 = ? 結果肯定不是原來的 5 了。

再者,6 臺服務集羣中,當某個主從集羣出現故障時,無法進行緩存,那我們需要把故障機器移除,所以取模又會從 6 變成了 5。我們計算的公式也會變化。

由於上面 hash 算法是使用取模來進行緩存的,爲了規避上述情況,Hash 一致性算法就誕生了。

一致性 Hash 算法原理

一致性 Hash 算法也是使用取模的方法,不過,上述的取模方法是對服務器的數量進行取模,而一致性的 Hash 算法是對 2^32取模。即,一致性 Hash 算法將整個 Hash 空間組織成一個虛擬的 圓環,Hash 函數的值空間爲0 ~ 2^32 -1(一個32位無符號整型),整個哈希環如下:

服務器在哈希環上的位置

現在,我們使用以下算法定位數據訪問到相應的服務器:

將數據 key 使用相同的函數 Hash 計算出哈希值,並確定此數據在環上的位置,從此位置沿環順時針查找,遇到的服務器就是其應該定位到的服務器。

例如,現在有 ObjectA、ObjectB、ObjectC 是哪個數據對象,經過哈希計算後,在環空間上的位置如下:

數據對象在環上的位置
根據一致性算法:

  • ObjectA -> NodeA
  • ObjectB -> NodeB
  • ObjectC -> NodeC

一致性 Hash 算法的容錯性和可擴展性

現在,假設我們的 NodeC 宕機了,我們從圖中可以看到,A、B 不會受到影響,只有 ObjectC 對象被重新定位到 NodeA。所以我們發現,在一致性 Hash 算法中,如果一臺服務器不可用,受影響的數據僅僅是此服務器到其環空間前一臺服務器之間的數據(這裏爲 NodeC 到 NodeB 之間的數據),其他不會受到影響。如圖所示:

C節點宕機情況,數據移到節點A上
另外一種情況,現在我們系統增加了一臺服務器 NodeX,如圖所示:

增加新的服務器節點X
此時對象 ObjectA、ObjectB 沒有受到影響,只有 ObjectC 重新定位到了新的節點 X 上。
如上所述:

一致性 Hash 算法對於節點的增減都只需要重定位環空間中的一小部分數據,有很好的容錯性和可擴展性。

數據傾斜問題

在一致性 Hash 算法服務節點太少的情況下,容易因爲幾點分佈不均勻造成數據傾斜(被緩存的對象大部分緩存在某一臺服務器上)的問題,如圖特例:

數據傾斜

這時我們發現有大量數據集中在節點 A 上,而節點 B 只有少量數據。爲了解決數據傾斜問題,一致性 Hash 算法引入了虛擬節點機制,即對每一個服務器節點計算多個哈希,每個計算結果未知都放置一個此服務節點,稱爲虛擬節點。

具體操作可以爲服務器 IP 或主機名後加入編號來實現,實現如圖所示:
增加虛擬節點情況
數據定位算法不變,只需要增加一步:虛擬節點到實際點的映射。
所以加入虛擬節點之後,即使在服務節點很少的情況下,也能做到數據的均勻分佈。

轉載於:一致性Hash原理與實現(Java)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章