概念
http://blog.codinglabs.org/articles/consistent-hashing.html
簡單總結:
普通分佈式緩存痛點:服務器宕機或擴容,數據重新hash計算影響大。
虛擬環:一致性hash算法是來做服務器的負載均衡,而服務器的IP地址是32位,所以是2^32-1次方的數值空間。
服務器尋址:h = Hash(ip或主機名) ,放入服務虛擬環中
數據定位服務器:採用服務器尋址相同算法h = Hash(i數據key) % N,數據順時針遇到的第一臺服務器
宕機容錯:隻影響此服務器環空間前一臺服務器(逆時針)的數據,數據重新hash計算定位即可
增加機器可擴展:隻影響新服務器環空間前一臺服務器(逆時針)的數據,數據重新hash計算定位即可
數據傾斜:虛擬節點解決,即服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點。
圖例:
例子:
數據結構:採用SortedMap,SortedMap<Long, T> circle = new TreeMap<Long, T>(),用到的方法如圖紅框所示,如果數據key的hash值不再服務虛擬環節點上,那麼使用tailMap(數據key的hash值)查找大於key的hash值的所有數據,然後使用firstKey找到相鄰的第一個健key,在由服務虛擬環節點獲取服務節點,存儲數據
代碼例子:
private final Hashing hash;// hash算法
private final int virtualNodeNum;// 虛擬績點
private final SortedMap<Long, T> circle = new TreeMap<Long, T>();// 服務節點hash環
public ConsistentHash(Hashing hash, int virtualNodeNum, Collection<T> nodes) {
this.hash = hash;
this.virtualNodeNum = virtualNodeNum;
for (T node : nodes) {
add(node);
}
}
/**
* 增加機器節點
*
* @param node
*/
public void add(T node) {
for (int i = 0; i < this.virtualNodeNum; i++) {
circle.put(this.hash.hash(node.toString() + i), node);
}
}
/**
* 取得真實機器節點
* key: 數據key
*/
public T get(String key) {
if (circle.isEmpty()) {
return null;
}
// 獲取hash值
long hash = this.hash.hash(key);
if (!circle.containsKey(hash)) {
// 沿環的順時針找到大於hash值的所有虛擬節點
SortedMap<Long, T> tailMap = circle.tailMap(hash);
// 用得到的所有虛擬節點判斷是否爲空,空則找到第一個機器節點,否則返回第一個節點
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
// 返回該虛擬節點對應的機器節點的信息
return circle.get(hash);
}