當我們在做資料庫分庫分表或者是分散式快取時,不可避免的都會遇到一個問題:
如何將資料均勻的分散到各個節點中,並且儘量的在加減節點時能使受影響的資料最少。
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 後加上編號來生成雜湊值。
這樣只需要在原有的基礎上多一步由虛擬節點對映到實際節點的步驟即可讓少量節點也能滿足均勻性。
號外
最近在總結一些 Java 相關的知識點,感興趣的朋友可以一起維護。