在分佈式存儲系統中,將數據分佈至多個節點的方式之一是使用哈希算法。假設初始節點數爲 N,則傳統的對 N 取模的映射方式存在一個問題在於:當節點增刪,即 N 值變化時,整個哈希表(Hash Table)須要從新映射,這便意味着大部分數據須要在節點之間移動。node
所以如今廣泛使用的是被稱爲一致性哈希(Consistent Hashing)的一類算法。「一致性」 這個定語的意義在於:當增刪節點時,隻影響到與變更節點相鄰的一個或兩個節點,散列表的其餘部分與原來保持一致。某種程度上能夠將其理解爲:一致性哈希算法的哈希函數與節點數 N 無關。算法
其餘地方對一致性哈希配圖的時候,都會選擇一個圓環來解釋,但我我的感受哈希表更加直觀:網絡
但這裏其實仍有改進的空間。負載均衡
問題在於,上面須要將 「節點 4」 的一半數據搬運到 「節點 5」 上,這個壓力會比較大。以一個節點存有 3TB 的數據、節點間網絡爲千兆網(但只容許搬運進程使用 25% 負載)來算,搬運完 1.5TB 的數據最少須要 (1.5TB * 1024GB/TB * 1024MB/GB) / (125MB/s * 0.25) ≈ 14h;另外一方面,「節點 5」 直接分擔走了 「節點 4」 數據的一半,若是原來 4 個節點的負載是均衡的(md5 自己是一個很均勻的哈希函數),那麼如今就變得不均衡了。分佈式
這兩個問題有一個公共的解決方法:新增的 「節點 5」 不僅從 「節點 4」 搬運數據,而從全部其餘節點(或子集)處搬運數據,同時還要繼續保持哈希一致性。函數
這種想法的一個實現方式就是,使用虛擬節點(virtual nodes)。上面 md5 哈希表實際能夠分爲兩段:google
當使用虛擬節點時,咱們保持第一段不變,但會在第二段將哈希值映射到物理節點的過程當中再插入一個虛擬節點中間件,從而將過程變爲:中間件
新哈希表的關鍵之處在於虛擬節點的數量比物理節點數多得多,甚至不少時候會將虛擬節點的數量設置爲 「儘量多」。這樣新哈希表的前兩段就固定不變了,當增刪物理節點時,只是對虛擬節點進行必要的從新分配的過程。進程
上圖中咱們依 md5 值的首位劃分了 16 個虛擬節點,而後將它們映射到 4 個物理節點。(實際應用中,即便你當下只有 10 個物理節點,也大能夠按 md5 的前三位劃分出 4096 個虛擬節點)當咱們增長物理 「節點 5」 的時候,就從節點 一、二、3 處各拿一個虛擬節點放到 「節點 5」 中。這個過程,「節點 5」 既可使用 100% 的網絡帶寬來接收數據;新的哈希表也實現了負載均衡。固然一致性也獲得了保證。md5
這種使用虛擬節點的一致性哈希算法我看到國內有人管它叫分佈式一致性哈希(Distributed Consistent Hashing),但這個 「分佈式」 叫法顯得有些不合適,由於這種改進只涉及到算法的實現而與哈希過程發生的位置無關,而且 google 上也找不到這種叫法。因此通常就稱改進的一致性哈希(Improved Consistent Hashing)好了。或者,使用虛擬節點的一致性哈希。