標籤:
前言:對於一致性雜湊已經不是罕見概念,在此只是對原有理論概念的一個整理和用自己的理解講述,希望對新手有些許協助,利人利己足矣。
1.概念
一致雜湊是一種特殊的雜湊演算法。在使用一致雜湊演算法後,雜湊表槽位元(大小)的改變平均只需要對 K/n 個關鍵字重新對應,其中 K是關鍵字的數量,n是槽位元量。然而在傳統的雜湊表中,添加或刪除一個槽位的幾乎需要對所有關鍵字進行重新對應。
一致雜湊將每個對象映射到圓環邊上的一個點,系統再將可用的節點機器映射到圓環的不同位置。尋找某個對象對應的機器時,需要用一致雜湊演算法計算得到對象對應圓環邊上位置,沿著圓環邊上尋找直到遇到某個節點機器,這台機器即為對象應該儲存的位置。 當刪除一台節點機器時,這台機器上儲存的所有對象都要移動到下一台機器。添加一台機器到圓環邊上某個點時,這個點的下一台機器需要將這個節點前對應的對象移動到新機器上。 更改對象在節點機器上的分布可以通過調整節點機器的位置來實現。
2.應用情境
在使用N台快取服務器時,一種常用的負載平衡方式是,對資源Object的請求使用 Hash(Object)%N 來映射到某一台快取服務器。但是缺點有二:
1.一個伺服器(伺服器A)Down掉之後,伺服器A中的緩衝資源都會失效,映射公式變成了Hash(Object)%(N-1), 緩衝失效,這會使得快取服務器大量集中地向原始內容伺服器更新緩衝。
2.當增加或減少一台快取服務器時,映射公式變成了Hash(Object)%(N+1),這種方式可能會改變所有資源對應的Hash值,也就是所有的緩衝都失效了,這會使得快取服務器大量集中地向原始內容伺服器更新緩衝。
因些,需要一致雜湊演算法來避免這樣的問題。 一致雜湊儘可能使同一個資源地圖到同一台快取服務器。這種方式要求增加一台快取服務器時,新的伺服器盡量分擔儲存其他所有伺服器的緩衝資源。減少一台快取服務器時,其他所有伺服器也可以盡量分擔儲存它的緩衝資源。 一致雜湊演算法的主要思想是將每個快取服務器與一個或多個雜湊範圍區間關聯起來,其中區間邊界通過計算快取服務器對應的雜湊值來決定。(定義區間的雜湊函數不一定和計算快取服務器雜湊值的函數相同,但是兩個函數的傳回值的範圍需要匹配。)如果一個快取服務器被移除,則它會從對應的區間會被併入到鄰近的區間,其他的快取服務器不需要任何改變。
3.原理簡析
3.1環形Hash空間
考慮通常的Hash演算法都是將value 映射到一個32位的 key 值上,也就是 0~2^32-1次方的數值空間。我們可以將這個空間想象成一個首( 0 )尾( 2^32-1 )相接的圓環,如1所示:
圖1 環形Hash空間
3.2把對象映射到環形Hash空間
下面考慮 4 個資來源物件 object1,object2,object3,bject4 ,通過 Hash Function Compute出的 Hash 值(key)在環上的分布 2 所示:
圖2 資來源物件的Key值分布
如此:
Key1 = Hash(Object1);
······
Key4 = Hash(Object4);
3.3把資原始伺服器映射到環形Hash空間
一致性Hash的基本思想就是將資來源物件和快取服務器(cache)都映射到同一個Hash數值空間中,並且使用相同的Hash演算法。快取服務器(cache)的Hash計算,一般的方法可以使用伺服器(cache)機器的IP地址或者機器名作為Hash輸入。
假設當前有 A,B 和 C 共 3 台快取服務器(cache) ,那麼其映射結果將 3 所示,他們在 hash 空間中,以對應的 hash值排列。
圖3 伺服器和資來源物件的Key值分布
如此:
KeyA = Hash(CacheA);
······
KeyC = Hash(CacheC);
3.4把資來源物件映射到快取服務器
現在伺服器(cache)和資來源物件都已經通過同一個Hash演算法映射到Hash數值空間中了,接下來要考慮的就是如何將對象映射到伺服器(cache)上面。
在這個環形空間中,如果沿著順時針方向從對象的 key 值出發,直到遇見一個伺服器(cache),那麼就將該Object Storage Service在這個快取服務器上,因為對象和伺服器的Hash 值是固定的,因此這個伺服器必然是唯一和確定的。這就是對象和伺服器的映射方法。
依然繼續上面的例子(參見圖 3 ),那麼根據上面的方法,對象 object1 將被儲存到 cache A 上; object2和 object3 對應到 cache C ; object4 對應到 cache B ;
3.5考慮伺服器的增減
根據上面的映射方法(順時針尋找)我們可以很容易的想象伺服器的增減導致資來源物件映射的改變情況。4和5所示:
圖4 快取服務器B被移除後的映射改變
圖5 添加快取服務器D後的映射改變
根據上文所述,一致性雜湊中節點的改變需要K/N個關鍵字的映射,N是快取服務器個數,K是資來源物件的個數,顯然4/3和4/4取整都為1,與以上結果相符。
3.6採用虛擬節點保證平衡性
在分布式緩衝中,都希望資來源物件能夠儘可能的均勻分布到快取服務器中,這也是平衡性的要求。但是,一致性雜湊並不能保證絕對的平衡性。舉個例子,在伺服器(cache)較少情況下,資來源物件並不能被均勻的映射到伺服器上,比如在上面的例
子中,僅部署 cache A 和 cache C 的情況下,在 4 個資來源物件中, cache A 僅儲存了 object1 ,而 cache C 則儲存了 object2 、 object3 和 object4 。顯然,分布是很不平衡的。
那麼,為解決這種情況,一致性雜湊引進了虛擬節點的概念。
虛擬節點( virtual node )是實際伺服器節點在Hash空間中的一個或多個複製品(replica),一實際個節點對應了若干個“虛擬節點”,這個對應個數也就是“複製個數”,“虛擬節點”在Hash空間中以Hash值排列。
仍以僅部署 cache A 和 cache C 的情況為例,在圖 4 中我們已經看到,cache節點分布並不均勻。現在我們引入虛擬節點,並設定“複製個數”為 2 ,這就意味著一共會存在 4 個“虛擬節點”, cache A1, cache A2 代表了cache A; cache C1, cache C2 代表了 cache C;假設一種比較理想的情況,6:
圖6 引入虛擬節點後的映射關係
此時,資來源物件到“虛擬節點”的映射關係為:objec1->cache A2 ; objec2->cache A1 ; objec3->cache C1 ; objec4->cache C2 ;因此對象 object1 和 object2 都被映射到了 cache A 上,而 object3 和 object4 映射到了 cache C 上;平衡性有了很大提高。
引入“虛擬節點”後,映射關係就從 { 對象 -> 節點 } 轉換到了 { 對象 -> 虛擬節點 } 。查詢物體所在 cache時的映射關係 7 所示:
圖7 查詢資來源物件的計算過程
虛擬節點的Hash值計算可以採用對應節點的 IP 位址加數字尾碼的方式。例如假設 cache A 的 IP 位址為202.168.14.241 。
引入虛擬節點前,計算 cache A 的 Hash 值:Hash(“202.168.14.241”);
引入虛擬節點後,計算虛擬節點cache A1 和的Hash值:Hash(“202.168.14.241#1”);
下面這個URL中講的很詳細:http://www.codeproject.com/Articles/56138/Consistent-hashing
4.C++實現
一致性雜湊還必須解決兩個問題,一個是用於節點儲存和尋找的資料結構的選擇,另一個是Hash演算法的選擇。
我們可以想象,節點是均勻的分布在你一個圓環上,也就是說節點Hash值可以儲存在一個有序隊列上。同時,該資料結構應該高效地支援節點頻繁的增刪,和理想的查詢效率,那麼自然會想到紅/黑樹狀結構,它是一顆近似平衡的二叉樹,因為操作比如插入、刪除和尋找某個值的最壞情況時間都要求與樹的高度成比例,這個在高度上的理論上限允許紅/黑樹狀結構在最壞情況下都是高效的,而不同於普通的二叉尋找樹。因此,用於節點儲存和尋找的資料結構我們選擇紅/黑樹狀結構,並實現其插入、刪除、尋找功能,並增加一個lookup函數,用於尋找key中最小的節點。
Hash演算法的選擇,一致性雜湊很重要的一點就是為瞭解決負載平衡問題,而虛擬節點是負載平衡的關鍵,所以,我們希望虛擬節點可以均勻地散步在圓環上。這裡我們選擇MD5演算法,通過MD5演算法,可以將一個標識串(用於標示虛擬結點)進行轉化,得到一個16位元組的字元數組,再對該數組進行處理,得到一個整型的Hash值。由於MD5具有高度的離散性,所以產生的Hash值也會具有很大的離散性,會均勻地散列到“環”上。
百聞不如一見,實際代碼連結:
http://files.cnblogs.com/coser/ConsistentHashAlgorithm.rar
5.PHP實現
下面是轉自http://blog.csdn.net/mayongzhan/article/details/4298834的PHP實現:
一致性雜湊(Consistent Hashing)