前言
在分布式系統中,常常需要使用緩存,而且通常是集群,訪問緩存和添加緩存都需要一個 hash 算法來尋找到合適的 Cache 節點。但,通常不是用取余hash,而是使用我們今天的主角—— 一致性 hash 算法。
今天樓主就來說說這個一致性 hash 算法。
1. 為什么普通的 hash 算法不行?
普通的 hash 算法通常都是對機器數量進行取余,比如集群環境中有 3 台 redis,當我們放入對象的時候,通常是對 3 進行取余。這種做法在大部分情況下是沒有問題的。但是,注意:如果緩存機器需要增減,問題就來了。
什么問題呢?
假設原本是 3 個 redis,這時候,加了一台 redis,那么取余算法就變成了取余 4。
這樣有什么問題呢?
答:當使用負載均衡的時候,負載均衡器根據對象的 key 對機器進行取余,這個時候,原有的 key 取余現有的機器數 4 就找不到那台機器了!笨一點的辦法,就是在增加機器的時候,清除所有緩存,但這會導致緩存擊穿甚至緩存雪崩,嚴重情況下引發 DB 宕機。
2. 一致性 hash 怎么解決這個問題?
很簡單,既然問題出在對機器取余上,那么就不對機器取余。
具體怎么做呢?
答:我們可以假設有一個 2 的 32 次方的環形,緩存節點通過 hash 落在環上。而對象的添加也是使用 hash,但很大的幾率是 hash 不到緩存節點的。怎么辦呢?找離他最近的那個節點。 比如順時針找前面那個節點。
能解決問題嗎?想象一下:當增減機器時,環形節點變化的只會影響一個節點,就是新節點的順時針方向的前面的節點。這個時候,我們只需要清除那一個節點的數據就足夠了,不用想取余 hash 那樣,清除所有節點的數據。
具體類似於下圖:

上圖中,節點中的五角星代表對象,紅綠黃代表節點,每個對象都會找他的上一個節點。如有增減,只影響一個節點。
如下圖所示:

紅色和綠色節點不受影響。
3. 一致性 hash 有什么問題呢?
是否這么做就完美了呢?
不是的。
如果認真看是上面的圖的話,會發現,黃色節點的負載壓力最大,這個集群環境負載不夠均衡。

什么原因導致的呢?原因是:如果緩存節點分布不均勻,就會出現這樣的情況。但是,你不能奢望是均勻的。
怎么辦呢?
我們可以在不均的地方給他弄均勻。在空閑的地方加入 虛擬節點,這些節點的數據映射到真實節點上,就可以了,如下圖所示:

上圖中,我們給每個節點都做了虛擬節點(虛線),從而讓整個集群在 hash 環比較均勻,從圖中也可以看出,這樣現對比之前均勻多了,黃色節點的負載和綠色節點額的負載相同。
4. 總結
總的來說,一致性 hash 還是比較簡單的。核心思想是,不使用對機器取余的算法。這樣就能避免機器增減帶來的影響。
同時,使用 就近尋址 的方式找到最近的節點。當然,這會引起負載不均衡,所以需要引入虛擬節點的方式,變相的增加節點,讓整個集群的負載能夠均衡。
后面,我們將自己寫一個一致性 hash 算法以加深印象。
good luck!!!!
