Redis詳解(九)——一致性hash算法


前言

一致性哈希算法(Consistent Hashing)在分布式系統的應用還是十分廣泛的,本文盡量結合業務場景快速講解一致性哈希算法的應用及與其相關的話題。

1 分布式緩存

隨着業務的擴展,流量的劇增,單體項目逐漸划分為分布式系統。對於經常使用的數據,我們可以使用Redis作為緩存機制,減少數據層的壓力。因此,重構后的系統架構如下圖所示:
簡單架構

優化最簡單的策略就是,把常用的數據保存到Redis中,為了實現高可用使用了3台Redis(沒有設置集群,集群至少要6台)。每次Redis請求會隨機發送到其中一台,但是這種策略會引發如下兩個問題:

  • 同一份數據可能在多個Redis數據庫,造成數據冗余
  • 某一份數據在其中一台Redis數據庫已存在,但是再次訪問Redis數據庫,並沒有命中數據已存在的庫。無法保證對相同的key的所有訪問都發送到相同的Redis中

要解決上述的問題,我們需要稍稍改變一些key存入Redis的規則:使用hash算法
例如,有三台Redis,對於每次的訪問都可以通過計算hash來求得hash值。
如公式 h=hash(key)%3,我們把Redis編號設置成0,1,2來保存對應hash計算出來的值,h的值等於Redis對應的編號。
但是hash算法也會面臨容錯性和擴展性的問題。容錯性是指當系統中的某個服務出現問題時,不能影響其他系統。擴展性是指當加入新的服務器后,整個系統能正確高效運行。

現假設有一台Redis服務器宕機了,那么為了填補空缺,要將宕機的服務器從編號列表中移除,后面的服務器按順序前移一位並將其編號值減一,此時每個key就要按h = Hash(key) % 2重新計算。

同樣,如果新增一台服務器,規則也同樣需要重新計算,h = Hash(key) % 4。因此,系統中如果有服務器更變,會直接影響到Hash值,大量的key會重定向到其他服務器中,造成緩存命中率降低,而這種情況在分布式系統中是十分糟糕的。

一個設計良好的分布式哈希方案應該具有良好的單調性,即服務節點的變更不會造成大量的哈希重定位。一致性哈希算法由此而生~

2 一致性哈希算法

一致哈希 是一種特殊的哈希算法。在使用一致哈希算法后,哈希表槽位數(大小)的改變平均只需要對 K/n 個關鍵字重新映射,其中K是關鍵字的數量, n是槽位數量。然而在傳統的哈希表中,添加或刪除一個槽位的幾乎需要對所有關鍵字進行重新映射。

簡單的說,一致性哈希是將整個哈希值空間組織成一個虛擬的圓環,如假設哈希函數H的值空間為0-2^32-1(哈希值是32位無符號整形),整個哈希空間環如下:

哈希環
整個空間按順時針方向組織,0和2^32-1在零點中方向重合。

接下來,把服務器按照IP或主機名作為關鍵字進行哈希,這樣就能確定其在哈希環的位置。
哈希環2
然后,我們就可以使用哈希函數H計算值為key的數據在哈希環的具體位置h,根據h確定在環中的具體位置,從此位置沿順時針滾動,遇到的第一台服務器就是其應該定位到的服務器。

例如我們有A、B、C、D四個數據對象,經過哈希計算后,在環空間上的位置如下:

哈希環3
根據一致性哈希算法,數據A會被定為到Server 1上,數據B被定為到Server 2上,而C、D被定為到Server 3上。

3 容錯性和擴展性

那么使用一致性哈希算法的容錯性和擴展性如何呢?

3.1 容錯性

假如RedisService2宕機了,那么會怎樣呢?

Redis2宕機

那么,數據B對應的節點保存到RedisService3中。因此,其中一台宕機后,干擾的只有前面的數據(原數據被保存到順時針的下一個服務器),而不會干擾到其他的數據。

3.2 擴展性

下面考慮另一種情況,假如增加一台服務器Redis4,具體位置如下圖所示:

RedisServicee4
原本數據C是保存到Redis3中,但由於增加了Redis4,數據C被保存到Redis4中。干擾的也只有Redis3而已,其他數據不會受到影響。

因此,一致性哈希算法對於節點的增減都只需重定位換空間的一小部分即可,具有較好的容錯性和可擴展性

4 虛擬節點

前面部分都是講述到Redis節點較多和節點分布較為均衡的情況,如果節點較少就會出現節點分布不均衡造成數據傾斜問題。

例如,我們的的系統有兩台Redis,分布的環位置如下圖所示:

哈希環
這會產生一種情況,Redis4的hash范圍比Redis3的hash范圍大,導致數據大部分都存儲在Redis4中,數據存儲不平衡。

為了解決這種數據存儲不平衡的問題,一致性哈希算法引入了虛擬節點機制,即對每個節點計算多個哈希值,每個計算結果位置都放置在對應節點中,這些節點稱為虛擬節點

具體做法可以在服務器IP或主機名的后面增加編號來實現,例如上面的情況,可以為每個服務節點增加三個虛擬節點,於是可以分為 RedisService1#1、 RedisService1#2、 RedisService1#3、 RedisService2#1、 RedisService2#2、 RedisService2#3,具體位置如下圖所示:

虛擬節點

對於數據定位的hash算法仍然不變,只是增加了虛擬節點到實際節點的映射。例如,數據C保存到虛擬節點Redis1#2,實際上數據保存到Redis1中。這樣,就能解決服務節點少時數據不平均的問題。在實際應用中,通常將虛擬節點數設置為32甚至更大,因此即使很少的服務節點也能做到相對均勻的數據分布


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM