一致 Hash 算法
當(dāng)我們在做數(shù)據(jù)庫分庫分表或者是分布式緩存時(shí),不可避免的都會遇到一個(gè)問題:
如何將數(shù)據(jù)均勻的分散到各個(gè)節(jié)點(diǎn)中,并且盡量的在加減節(jié)點(diǎn)時(shí)能使受影響的數(shù)據(jù)最少。
Hash 取模
隨機(jī)放置就不說了,會帶來很多問題。通常最容易想到的方案就是?hash 取模了。
可以將傳入的 Key 按照?index = hash(key) % N?這樣來計(jì)算出需要存放的節(jié)點(diǎn)。其中 hash 函數(shù)是一個(gè)將字符串轉(zhuǎn)換為正整數(shù)的哈希映射方法,N 就是節(jié)點(diǎn)的數(shù)量。
這樣可以滿足數(shù)據(jù)的均勻分配,但是這個(gè)算法的容錯(cuò)性和擴(kuò)展性都較差。
比如增加或刪除了一個(gè)節(jié)點(diǎn)時(shí),所有的 Key 都需要重新計(jì)算,顯然這樣成本較高,為此需要一個(gè)算法滿足分布均勻同時(shí)也要有良好的容錯(cuò)性和拓展性。
一致 Hash 算法
一致 Hash 算法是將所有的哈希值構(gòu)成了一個(gè)環(huán),其范圍在?0 ~ 2^32-1。如下圖:
之后將各個(gè)節(jié)點(diǎn)散列到這個(gè)環(huán)上,可以用節(jié)點(diǎn)的 IP、hostname 這樣的唯一性字段作為 Key 進(jìn)行?hash(key),散列之后如下:
之后需要將數(shù)據(jù)定位到對應(yīng)的節(jié)點(diǎn)上,使用同樣的?hash 函數(shù)?將 Key 也映射到這個(gè)環(huán)上。
這樣按照順時(shí)針方向就可以把 k1 定位到?N1節(jié)點(diǎn),k2 定位到?N3節(jié)點(diǎn),k3 定位到?N2節(jié)點(diǎn)。
容錯(cuò)性
這時(shí)假設(shè) N1 宕機(jī)了:
依然根據(jù)順時(shí)針方向,k2 和 k3 保持不變,只有 k1 被重新映射到了 N3。這樣就很好的保證了容錯(cuò)性,當(dāng)一個(gè)節(jié)點(diǎn)宕機(jī)時(shí)只會影響到少少部分的數(shù)據(jù)。
拓展性
當(dāng)新增一個(gè)節(jié)點(diǎn)時(shí):
在 N2 和 N3 之間新增了一個(gè)節(jié)點(diǎn) N4 ,這時(shí)會發(fā)現(xiàn)受印象的數(shù)據(jù)只有 k3,其余數(shù)據(jù)也是保持不變,所以這樣也很好的保證了拓展性。
虛擬節(jié)點(diǎn)
到目前為止該算法依然也有點(diǎn)問題:
當(dāng)節(jié)點(diǎn)較少時(shí)會出現(xiàn)數(shù)據(jù)分布不均勻的情況:
這樣會導(dǎo)致大部分?jǐn)?shù)據(jù)都在 N1 節(jié)點(diǎn),只有少量的數(shù)據(jù)在 N2 節(jié)點(diǎn)。
為了解決這個(gè)問題,一致哈希算法引入了虛擬節(jié)點(diǎn)。將每一個(gè)節(jié)點(diǎn)都進(jìn)行多次 hash,生成多個(gè)節(jié)點(diǎn)放置在環(huán)上稱為虛擬節(jié)點(diǎn):
計(jì)算時(shí)可以在 IP 后加上編號來生成哈希值。
這樣只需要在原有的基礎(chǔ)上多一步由虛擬節(jié)點(diǎn)映射到實(shí)際節(jié)點(diǎn)的步驟即可讓少量節(jié)點(diǎn)也能滿足均勻性。
總結(jié)
以上是生活随笔為你收集整理的一致 Hash 算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何成为一位「不那么差」的程序员
- 下一篇: Fluentd初探 简介与安装