當我們在做數據庫分庫分表或者是分佈式緩存時,不可避免的都會遇到一個問題:
如何將數據均勻的分散到各個節點中,並且儘量的在加減節點時能使受影響的數據最少。
Hash 取模
隨機放置就不說了,會帶來很多問題。通常最容易想到的方案就是 hash 取模
了。
可以將傳入的 Key 按照 index = hash(key) % N
這樣來計算出需要存放的節點。其中 hash 函數是一個將字符串轉換爲正整數的哈希映射方法,N 就是節點的數量。
這樣可以滿足數據的均勻分配,但是這個算法的容錯性和擴展性都較差。
比如增加或刪除了一個節點時,所有的 Key 都需要重新計算,顯然這樣成本較高,爲此需要一個算法滿足分佈均勻同時也要有良好的容錯性和拓展性。
一致 Hash 算法
一致 Hash 算法是將所有的哈希值構成了一個環,其範圍在 0 ~ 2^32-1
。如下圖:
之後將各個節點散列到這個環上,可以用節點的 IP、hostname 這樣的唯一性字段作爲 Key 進行 hash(key)
,散列之後如下:
之後需要將數據定位到對應的節點上,使用同樣的 hash 函數
將 Key 也映射到這個環上。
這樣按照順時針方向就可以把 k1 定位到 N1節點
,k2 定位到 N3節點
,k3 定位到 N2節點
。
容錯性
這時假設 N1 宕機了:
依然根據順時針方向,k2 和 k3 保持不變,只有 k1 被重新映射到了 N3。這樣就很好的保證了容錯性,當一個節點宕機時只會影響到少少部分的數據。
拓展性
當新增一個節點時:
在 N2 和 N3 之間新增了一個節點 N4 ,這時會發現受印象的數據只有 k3,其餘數據也是保持不變,所以這樣也很好的保證了拓展性。
虛擬節點
到目前爲止該算法依然也有點問題:
當節點較少時會出現數據分佈不均勻的情況:
這樣會導致大部分數據都在 N1 節點,只有少量的數據在 N2 節點。
爲了解決這個問題,一致哈希算法引入了虛擬節點。將每一個節點都進行多次 hash,生成多個節點放置在環上稱爲虛擬節點:
計算時可以在 IP 後加上編號來生成哈希值。
這樣只需要在原有的基礎上多一步由虛擬節點映射到實際節點的步驟即可讓少量節點也能滿足均勻性。