Java語言Consistent Hash演算法學習筆記(程式碼範例),consistenthash
本文研究的主要是ConsistentHashing演算法代碼。
一致性雜湊(Consistent Hash)協議簡介
一致性雜湊演算法在1997年由麻省理工學院提出(參見0),設計目標是為瞭解決網際網路中的熱點(Hot pot)問題,初衷和CARP十分類似。一致性雜湊修正了CARP使用的簡單雜湊演算法帶來的問題,使得DHT可以在P2P環境中真正得到應用。
雜湊演算法
一致性雜湊提出了在動態變化的Cache環境中,雜湊演算法應該滿足的4個適應條件:
平衡性(Balance)
平衡性是指雜湊的結果能夠儘可能分布到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。很多雜湊演算法都能夠滿足這一條件。
單調性(Monotonicity)
單調性是指如果已經有一些內容通過雜湊指派到了相應的緩衝中,又有新的緩衝加入到系統中。雜湊的結果應能夠保證原有已指派的內容可以被映射到新的緩衝中去,而不會被映射到舊的緩衝集合中的其他緩衝區。
簡單的雜湊演算法往往不能滿足單調性的要求,如最簡單的線性雜湊:
x → ax + b mod (P)
在上式中,P表示全部緩衝的大小。不難看出,當緩衝大小發生變化時(從P1到P2),原來所有的雜湊結果均會發生變化,從而不滿足單調性的要求。
雜湊結果的變化意味著當緩衝空間發生變化時,所有的映射關係需要在系統內全部更新。而在P2P系統內,緩衝的變化等價於Peer加入或退出系統,這一情況在P2P系統中會頻繁發生,因此會帶來極大計算和傳輸負荷。單調性就是要求雜湊演算法能夠避免這一情況的發生。
分散性(Spread)
在分布式環境中,終端有可能看不到所有的緩衝,而是只能看到其中的一部分。當終端希望通過雜湊過程將內容映射到緩衝上時,由於不同終端所見的緩衝範圍有可能不同,從而導致雜湊的結果不一致,最終的結果是相同的內容被不同的終端映射到不同的緩衝區中。這種情況顯然是應該避免的,因為它導致相同內容被儲存到不同緩衝中去,降低了系統儲存的效率。分散性的定義就是上述情況發生的嚴重程度。好的雜湊演算法應能夠盡量避免不一致的情況發生,也就是盡量降低分散性。
負載(Load)
負載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內容映射到不同的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不同的使用者映射為不同的內容。與分散性一樣,這種情況也是應當避免的,因此好的雜湊演算法應能夠盡量降低緩衝的負荷。
從表面上看,一致性雜湊針對的是分布式緩衝的問題,但是如果將緩衝看作P2P系統中的Peer,將映射的內容看作各種共用的資源(資料,檔案,媒體流等),就會發現兩者實際上是在描述同一問題。
路由演算法
在一致性雜湊演算法中,每個節點(對應P2P系統中的Peer)都有隨機分配的ID。在將內容映射到節點時,使用內容的關鍵字和節點的ID進行一致性雜湊運算並獲得索引值。一致性雜湊要求索引值和節點ID處於同一範圍。最簡單的索引值和ID可以是一維的,比如從0000到9999的整數集合。
根據KVStore for Redis內容時,內容將被儲存到具有與其索引值最接近的ID的節點上。例如索引值為1001的內容,系統中有ID為1000,1010,1100的節點,該內容將被映射到1000節點。
為了構建查詢所需的路由,一致性雜湊要求每個節點儲存其上行節點(ID值大於自身的節點中最小的)和下行節點(ID值小於自身的節點中最大的)的位置資訊(IP地址)。當節點需要尋找內容時,就可以根據內容的索引值決定向上行或下行節點發起查詢請求。收到查詢請求的節點如果發現自己擁有被請求的目標,可以直接向發起查詢請求的節點返回確認;如果發現不屬於自身的範圍,可以轉寄請求到自己的上行/下行節點。
為了維護上述路由資訊,在節點加入/退出系統時,相鄰的節點必須及時更新路由資訊。這就要求節點不僅儲存直接相連的下行節點位置資訊,還要知道一定深度(n跳)的間接下行節點資訊,並且動態地維護節點列表。當節點退出系統時,它的上行節點將嘗試直接連接到最近的下行節點,串連成功後,從新的下行節點獲得下行節點列表並更新自身的節點列表。同樣的,當新的節點加入到系統中時,首先根據自身的ID找到下行節點並獲得下行節點列表,然後要求上行節點修改其下行節點列表,這樣就恢複了路由關係。
討論
一致性雜湊基本解決了在P2P環境中最為關鍵的問題——如何在動態網路拓撲中分布儲存和路由。每個節點僅需維護少量相鄰節點的資訊,並且在節點加入/退出系統時,僅有相關的少量節點參與到拓撲的維護中。所有這一切使得一致性雜湊成為第一個實用的DHT演算法。
但是一致性雜湊的路由演算法尚有不足之處。在查詢過程中,查詢訊息要經過O(N)步(O(N)表示與N成正比關係,N代表系統內的節點總數)才能到達被查詢的節點。不難想象,當系統規模非常大時,節點數量可能超過百萬,這樣的查詢效率顯然難以滿足使用的需要。換個角度來看,即使使用者能夠忍受漫長的時延,查詢過程中產生的大量訊息也會給網路帶來不必要的負荷。
原始碼:
package heritrix;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;}//計算hash值int hash = hashFunction.hash(key);//如果不包括這個hash值if(!circle.containsKey(hash)){SortedMap<Integer, T> tailMap = circle.tailMap(hash);hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();}return circle.get(hash);}}總結
以上就是本文關於Java語言Consistent Hash演算法學習筆記(程式碼範例)的全部內容,希望對大家有所協助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支援!