概念
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);
}