Redis 集羣的使用
在使用 Redis 的過程中,爲了保證 Redis 的高可用,我們一般會對 Redis 做主從複製,組成Master-Master
或者 Master-Slave
的形式,進行數據的讀寫分離。
舉個例子,我們有一個電商平臺,需要使用 Redis 存儲商品的圖片資源,存儲的格式爲鍵值對, key 值爲圖片名稱,value 爲該圖片所在的文件服務器的路徑,我們需要根據文件名,查找文件所在的文件服務器上的路徑,我們的圖片數量大概在 3000w 左右,按照我們的規則進行分庫,規則就是隨機分配的,我們以每臺服務器存 500w 的數量,部署 12 臺緩存服務器,並且進行主從複製,架構圖如下圖所示:
由於我們定義的規則是隨機的,所以我們的數據有可能存儲在任何一組 Redis 中,比如我們需要查詢“produc.png”的圖片,由於規則的隨機性,我們需要遍歷所有 Redis 服務器,才能查詢得到。這樣的結果顯然不是我們所需要的。所以我們會想到按某一個字段值進行 Hash 值、取模。所以我們就看看使用 Hash 的方式是怎麼進行的。
使用 Hash 的 Redis 集羣
如果我們使用 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 之間的數據),其他不會受到影響。如圖所示:
另外一種情況,現在我們系統增加了一臺服務器 NodeX,如圖所示:
此時對象 ObjectA、ObjectB 沒有受到影響,只有 ObjectC 重新定位到了新的節點 X 上。
如上所述:
一致性 Hash 算法對於節點的增減都只需要重定位環空間中的一小部分數據,有很好的容錯性和可擴展性。
數據傾斜問題
在一致性 Hash 算法服務節點太少的情況下,容易因爲幾點分佈不均勻造成數據傾斜(被緩存的對象大部分緩存在某一臺服務器上)的問題
,如圖特例:
這時我們發現有大量數據集中在節點 A 上,而節點 B 只有少量數據。爲了解決數據傾斜問題,一致性 Hash 算法引入了虛擬節點機制
,即對每一個服務器節點計算多個哈希,每個計算結果未知都放置一個此服務節點,稱爲虛擬節點。
具體操作可以爲服務器 IP 或主機名後加入編號來實現,實現如圖所示:
數據定位算法不變,只需要增加一步:虛擬節點到實際點的映射。
所以加入虛擬節點之後,即使在服務節點很少的情況下,也能做到數據的均勻分佈。