一致性哈希算法是在1997年由麻省理工學院的Karger等人爲了解決熱點(Hot spot)問題而提出的. 一致性哈希算法在分佈式系統中有很是普遍的使用, 好比如何將數據進行分區, 如何將數據映射到對應的緩存中. 本文會首先介紹採用通常的hash算法會有什麼問題, 而後在提出一致性hash算法的原理以及一致性hash算法的好處以及缺點.redis
爲了說明通常的hash算法和一致性hash算法, 咱們這裏假設一個實際的場景. 採用redis集羣來進行說明算法
在redis集羣中, 若是數據量比較大, 因爲沒法在一個節點將全部的數據所有保存, 咱們會將數據進行分區. 假設以下圖所示, 採用了四個分區:數據庫
若是咱們將數據隨機的分配到不一樣的分區中, 那麼查詢指定的數據就須要遍歷全部的分區, 因此咱們須要算法來告訴咱們將數據放置到那個分區中.後端
咱們常見的hash算法就是採用取模運算. 好比針對上面有四個分區的集羣, 咱們能夠將數據對4取模, 則就能夠將數據指定到對應的分區中.緩存
雖然這種hash方法提高了性能, 可是它也有很嚴重的問題. 主要是在服務器數量變更的時候, 全部的緩存位置都要發生變化.服務器
假設如今因爲數據量增長比較多, 須要增長緩存服務器. 假設增長了1臺服務器, 則服務器從4臺變成5臺. 那麼以前的對4取模如今變成對5取模. 能夠想象數據的取模結果都發生了變化. 這樣形成咱們沒法從緩存中查詢到數據, 在必定的時間範圍內,緩存中的數據是失效的, 那麼請求就會落到後端的數據庫中, 形成緩存雪崩.分佈式
或者若是緩存中有一臺服務器出現故障, 那麼服務器從4臺變成3臺, 仍然存在同樣的問題.函數
爲了解決上面說到的問題, 出現了一致性hash算法.性能
一致性hash算法也是採用取模的方法, 可是不一樣於對服務器數量取模, 而是對2^32取模. 就是一致性hash算法將整個哈希值空間組織成一個虛擬的圓環, 圓環以下: 對象
整個空間按順時針方法組織, 圓環的正上方表明0, 0右側的第一個點表明1, 依次類推, 之道2^32 -1. 組成的這個圓環稱爲hash環.
下一步是將各個服務器hash到圓環上對應的點上, 具體能夠選擇服務器的IP地址或服務器主機名等. 好比將上文中提到的四個服務器對應到圓環上,以下:
接下來就是定位數據訪問到哪一個服務器上, 將數據的可使用相同的hash函數(就是對2^32取模)計算出hash值, 肯定數據在hash環上的位置, 今後位置沿環順時針"行走", 遇到的第一個服務器就是其應該定位的服務器!
例如咱們有Object A、Object B、Object C、Object D四個數據對象,通過哈希計算後,在環空間上的位置以下:
根據一致性Hash算法,數據A會被定爲到Node A上,B被定爲到Node B上,C被定爲到Node C上,D被定爲到Node D上。
現假設Node C不幸宕機,能夠看到此時對象A、B、D不會受到影響,只有C對象被重定位到Node D。通常的,在一致性Hash算法中,若是一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響,以下所示:
下面考慮另一種狀況,若是在系統中增長一臺服務器Node X,以下圖所示:
此時對象Object A、B、D不受影響,只有對象C須要重定位到新的Node X !通常的,在一致性Hash算法中,若是增長一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即沿着逆時針方向行走遇到的第一臺服務器)之間數據,其它數據也不會受到影響。
綜上所述,一致性Hash算法對於節點的增減都只需重定位環空間中的一小部分數據,具備較好的容錯性和可擴展性。
一致性Hash算法在服務節點太少時,容易由於節點分部不均勻而形成數據傾斜(被緩存的對象大部分集中緩存在某一臺服務器上)問題,例如系統中只有兩臺服務器,其環分佈以下:
此時必然形成大量數據集中到Node A上,而只有極少許會定位到Node B上。爲了解決這種數據傾斜問題,一致性Hash算法引入了虛擬節點機制,即對每個服務節點計算多個哈希,每一個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體作法能夠在服務器IP或主機名的後面增長編號來實現。
例如上面的狀況,能夠爲每臺服務器計算三個虛擬節點,因而能夠分別計算 「Node A#1」、「Node A#2」、「Node A#3」、「Node B#1」、「Node B#2」、「Node B#3」的哈希值,因而造成六個虛擬節點:
同時數據定位算法不變,只是多了一步虛擬節點到實際節點的映射,例如定位到「Node A#1」、「Node A#2」、「Node A#3」三個虛擬節點的數據均定位到Node A上。這樣就解決了服務節點少時數據傾斜的問題。在實際應用中,一般將虛擬節點數設置爲32甚至更大,所以即便不多的服務節點也能作到相對均勻的數據分佈。