在做伺服器負載平衡時候可供選擇的負載平衡的演算法有很多,包括: 輪循演算法(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演算法為例說明。
由於hash演算法結果一般為unsigned int型,因此對於hash函數的結果應該均勻分布在[0,232-1]間,如果我們把一個圓環用232 個點來進行均勻切割,首先按照hash(key)函數算出伺服器(節點)的雜湊值, 並將其分布到0~232的圓上。
用同樣的hash(key)函數求出需要儲存資料的鍵的雜湊值,並映射到圓上。然後從資料對應到的位置開始順時針尋找,將資料儲存到找到的第一個伺服器(節點)上。
Consistent Hashing原理示意圖
新增一個節點的時候,只有在圓環上新增節點逆時針方向的第一個節點的資料會受到影響。刪除一個節點的時候,只有在圓環上原來刪除節點順時針方向的第一個節點的資料會受到影響,因此通過Consistent Hashing很好地解決了負載平衡中由於新增節點、刪除節點引起的hash值顛簸問題。
Consistent Hashing添加伺服器示意圖
虛擬節點(virtual nodes):之所以要引進虛擬節點是因為在伺服器(節點)數較少的情況下(例如只有3台伺服器),通過hash(key)算出節點的雜湊值在圓環上並不是均勻分布的(稀疏的),仍然會出現各節點負載不均衡的問題。虛擬節點可以認為是實際節點的複製品(replicas),本質上與實際節點實際上是一樣的(key並不相同)。引入虛擬節點後,通過將每個實際的伺服器(節點)數按照一定的比例(例如200倍)擴大後並計算其hash(key)值以均勻分布到圓環上。在進行負載平衡時候,落到虛擬節點的雜湊值實際就落到了實際的節點上。由於所有的實際節點是按照相同的比例複製成虛擬節點的,因此解決了節點數較少的情況下雜湊值在圓環上均勻分布的問題。
虛擬節點對Consistent Hashing結果的影響
從上圖可以看出,在節點數為10個的情況下,每個實際節點的虛擬節點數為實際節點的100-200倍的時候,結果還是很均衡的。
第3段中有這些文字:“但這樣的演算法方法存在致命問題,如果某一台機器宕機,那麼應該落在該機器的請求就無法得到正確的處理,這時需要將當掉的伺服器從演算法從去除,此時候會有(N-1)/N的伺服器的快取資料需要重新進行計算;”
為何是 (N-1)/N 呢?解釋如下:
比如有 3 台機器,hash值 1-6 在這3台上的分布就是:
host 1: 1 4
host 2: 2 5
host 3: 3 6
如果掛掉一台,只剩兩台,模數取 2 ,那麼分布情況就變成:
host 1: 1 3 5
host 2: 2 4 6
可以看到,還在資料位元置不變的只有2個: 1,2,位置發生改變的有4個,占共6個資料的比率是 4/6 = 2/3這樣的話,受影響的資料太多了,勢必太多的資料需要重新從 DB 載入到 cache 中,嚴重影響效能
【consistent hashing 的辦法】
上面提到的 hash 模數,模數取的比較小,一般是負載的數量,而 consistent hashing 的本質是將模數取的比較大,為 2的32次方減1,即一個最大的 32 位整數。然後,就可以從容的安排資料導向了,那個圖還是挺直觀的。
以下部分為一致性雜湊演算法的一種PHP實現。點擊下載