(轉)redis系列之——一致性hash算法

數據分片(sharding)
分佈式數據存儲時,經常要考慮數據分片,避免將大量的數據放在單表或單庫中,造成查詢等操作的耗時過長。比如,存儲訂單數據時使用三個mysql庫(編號0,1,2),當一條訂單數據過來時,對訂單id求hash後與機器數量取模,hash(orderId) % 3,假如得到的結果是2,則這條數據會存儲到編號爲2的mysql中。分表分庫存儲時,根據數據庫的主鍵或唯一鍵做hash,然後跟數據庫機器的數量取模,從而決定該條數據放在哪個庫中。

根據機器數量取模就會存在一個問題,當機器不夠用需要擴容或機器宕機,機器的數量就會發生變化,造成數據的命中率下降,所以之前的數據就需要重新hash做一次sharding。這種操作會導致服務在一定的時間不可用,而且每次擴縮容都會存在這個問題。

一致性hash
一致性hash算法主要應用於分佈式存儲系統中,可以有效地解決分佈式存儲結構下普通餘數Hash算法帶來的伸縮性差的問題,可以保證在動態增加和刪除節點的情況下儘量有多的請求命中原來的機器節點。

Hash環
一致性Hash算法也是使用取模的方法,只是,剛纔描述的取模法是對服務器的數量進行取模,而一致性Hash算法是對2^ 32-1取模,什麼意思呢簡單來說,一致性Hash算法將整個Hash值控件組織成一個虛擬的圓環,如假設某哈希函數H的值空間爲0-2^32-1取模(即哈希值是一個32位無符號整型),整個哈希環如下:

 

 

 整個空間按順時針方向組織,圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,2、3、4、5、6……直到2^ 32-1,也就是說0點左側的第一個點代表2^ 32-1, 0和2^ 32-1在零點中方向重合,我們把這個由2^32個點組成的圓環稱爲Hash環。

下一步將各個服務器使用Hash進行一個哈希,具體可以選擇服務器的主機名(考慮到ip變動,不要使用ip)作爲關鍵字進行哈希,這樣每臺機器就能確定其在哈希環上的位置,這裏假設將上文中三個master節點的IP地址哈希後在環空間的位置如下:

 

 

 下面將三條key-value數據也放到環上:將數據key使用相同的函數Hash計算出哈希值,並確定此數據在環上的位置。將數據從所在位置順時針找第一臺遇到的服務器節點,這個節點就是該key存儲的服務器!

例如我們有a、b、c三個key,經過哈希計算後,在環空間上的位置如下:key-a存儲在node1,key-b存儲在node2,key-c存儲在node3。

 

 

 容錯性和可擴展性

現假設Node 2不幸宕機,可以看到此時對象key-a和key-c不會受到影響,只有key-b被重定位到Node 3。一般的,在一致性Hash算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿着逆時針方向行走遇到的第一臺服務器,如下圖中Node 2與Node 1之間的數據,圖中受影響的是key-2)之間數據,其它不會受到影響。

同樣的,如果集羣中新增一個node 4,受影響的數據是node 1和node 4之間的數據,其他的數據是不受影響的。

 

 

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

數據傾斜
一致性Hash算法在服務節點太少時,容易因爲節點分部不均勻而造成數據傾斜(被緩存的對象大部分集中緩存在某一臺服務器上)問題,例如系統中只有兩臺服務器,此時必然造成大量數據集中到Node 2上,而只有極少量會定位到Node 1上。其環分佈如下:

 

 

 爲了解決數據傾斜問題,一致性Hash算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體做法可以在主機名的後面增加編號來實現。例如上面的情況,可以爲每臺服務器計算三個虛擬節點,於是可以分別計算 “Node 1#1”、“Node 1#2”、“Node 1#3”、“Node 2#1”、“Node 2#2”、“Node 2#3”的哈希值,於是形成六個虛擬節點:

 

 

 上圖中虛擬節點node 1#1,node 1#2,node 1#3都屬於真實節點node 1;虛擬節點node 2#1,node 2#2,node 2#3都屬於真實節點node 2。

實際項目中使用
上來先說一個誤區,**Redis 集羣沒有使用一致性hash, 而是引入了哈希槽slots的概念。**可以參考我的另一篇文章《redis系列之——高可用(主從、哨兵、集羣)》。

我們說的一致性hash都不是緩存機器自身的功能,而是集羣前置的代理或客戶端實現的。而redis官方的集羣是集羣本身通過slots實現了數據分片。

redis集羣時3.0版本纔出現的,出現的比較晚,在集羣模式出現之前,很多公司都做了自己的redis集羣了。這些自研的redis集羣的實現方式有多種,比如在redis的jedis客戶端jar包就是實現了一致性hash算法(客戶端模式),或者在redis集羣前面加上一層前置代理如Twemproxy也實現了hash一致性算法(代理模式)。Twemproxy,是 Twitter 開源出來的 Redis 和 Memcached 代理,使用這種代理模式搭建的集羣,我們的客戶端連接只需要連接代理服務器即可,不用連接代理後面具體的redis機器。Twemproxy具體使用哪一種hash算法也是可以通過配置文件指定的。
————————————————
版權聲明:本文爲CSDN博主「諸葛小猿」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wuxiaolongah/article/details/107327803

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