1. 程式人生 > >Consistent Hashing 一致性hash演算法

Consistent Hashing 一致性hash演算法

在做伺服器負載均衡時候可供選擇的負載均衡的演算法有很多,包括:  輪循演算法(Round Robin)、雜湊演算法(HASH)、最少連線演算法(Least Connection)、響應速度演算法(Response Time)、加權法(Weighted )等。其中雜湊演算法是最為常用的演算法.

    典型的應用場景是: 有N臺伺服器提供快取服務,需要對伺服器進行負載均衡,將請求平均分發到每臺伺服器上,每臺機器負責1/N的服務。

    常用的演算法是對hash結果取餘數 (hash() mod N):對機器編號從0到N-1,按照自定義的 hash()演算法,對每個請求的hash()值按N取模,得到餘數i,然後將請求分發到編號為i的機器。但這樣的演算法方法存在致命問題,如果某一臺機器宕 機,那麼應該落在該機器的請求就無法得到正確的處理,這時需要將當掉的伺服器從演算法從去除,此時候會有(N-1)/N的伺服器的快取資料需要重新進行計 算;如果新增一臺機器,會有N /(N+1)的伺服器的快取資料需要進行重新計算。對於系統而言,這通常是不可接受的顛簸(因為這意味著大量快取的失效或者資料需要轉移)。那麼,如何設 計一個負載均衡策略,使得受到影響的請求儘可能的少呢?
    在Memcached、

Key-Value Store、Bittorrent DHT、LVS中都採用了Consistent Hashing演算法,可以說Consistent Hashing 是分散式系統負載均衡的首選演算法。

1、Consistent Hashing演算法描述

    下面以Memcached中的Consisten Hashing演算法為例說明(參考memcached的分散式演算法)。

    由於hash演算法結果一般為unsigned int型,因此對於hash函式的結果應該均勻分佈在[0,232-1]間,如果我們把一個圓環用232 

個點來進行均勻切割,首先按照hash(key)函式算出伺服器(節點)的雜湊值, 並將其分佈到0~232的圓上。

    用同樣的hash(key)函式求出需要儲存資料的鍵的雜湊值,並對映到圓上。然後從資料對映到的位置開始順時針查詢,將資料儲存到找到的第一個伺服器(節點)上。

Consistent hashing,memcached,load balancing,負載均衡,演算法,key-value store Consistent Hashing原理示意圖

    新增一個節點的時候,只有在圓環上新增節點逆時針方向的第一個節點的資料會受到影響。刪除一個節點的時候,只有在圓環上原來刪除節點順時針方向的第一個節 點的資料會受到影響,因此通過Consistent Hashing很好地解決了負載均衡中由於新增節點、刪除節點引起的hash值顛簸問題。

Consistent hashing,memcached,load balancing,負載均衡,演算法,key-value store Consistent Hashing新增伺服器示意圖

    虛擬節點(virtual nodes):之所以要引進虛擬節點是因為在伺服器(節點)數較少的情況下 (例如只有3臺伺服器),通過hash(key)算出節點的雜湊值在圓環上並不是均勻分佈的(稀疏的),仍然會出現各節點負載不均衡的問題。虛擬節點可以 認為是實際節點的複製品(replicas),本質上與實際節點實際上是一樣的(key並不相同)。引入虛擬節點後,通過將每個實際的伺服器(節點)數按 照一定的比例(例如200倍)擴大後並計算其hash(key)值以均勻分佈到圓環上。在進行負載均衡時候,落到虛擬節點的雜湊值實際就落到了實際的節點 上。由於所有的實際節點是按照相同的比例複製成虛擬節點的,因此解決了節點數較少的情況下雜湊值在圓環上均勻分佈的問題。

Consistent hashing,memcached,load balancing,負載均衡,演算法,key-value store

虛擬節點對Consistent Hashing結果的影響

    從上圖可以看出,在節點數為10個的情況下,每個實際節點的虛擬節點數為實際節點的100-200倍的時候,結果還是很均衡的。

2、Consistent Hashing演算法實現:

    文章Consistent Hashing中描述了Consistent Hashing的Java實現,很簡潔。

import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;

public class ConsistentHash<T> {

 private final HashFunction hashFunction;
 private final int numberOfReplicas;
 private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();

 public ConsistentHash(HashFunction hashFunction, int numberOfReplicas,
     Collection<T> nodes) {
   this.hashFunction = hashFunction;
   this.numberOfReplicas = numberOfReplicas;

   for (T node : nodes) {
     add(node);
   }
 }

 public void add(T node) {
   for (int i = 0; i < numberOfReplicas; i++) {
     circle.put(hashFunction.hash(node.toString() + i), node);
   }
 }

 public void remove(T node) {
   for (int i = 0; i < numberOfReplicas; i++) {
     circle.remove(hashFunction.hash(node.toString() + i));
   }
 }

 public T get(Object key) {
   if (circle.isEmpty()) {
     return null;
   }
   int hash = hashFunction.hash(key);
   if (!circle.containsKey(hash)) {
     SortedMap<Integer, T> tailMap = circle.tailMap(hash);
     hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
   }
   return circle.get(hash);
 }

}

文章Consistent hashing implemented simply in Python描述了Consistent Hashing演算法的python 實現

 

3、參考文件

    http://weblogs.java.net/blog/2007/11/27/consistent-hashing
    http://michaelnielsen.org/blog/consistent-hashing/
    http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/

    http://tech.idv2.com/2008/07/24/memcached-004/ 

    http://amix.dk/blog/viewEntry/19367

    http://amix.dk/blog/viewEntry/19369 

    http://www.javaworld.com/javaworld/jw-10-2008/jw-10-load-balancing-1.html


轉自:http://www.yeeach.com/2009/10/02/consistent-hashing%E7%AE%97%E6%B3%95/