logo头像
Snippet 博客主题

一致性hash算法

本文于 1121 天之前发表,文中内容可能已经过时。

主要用途

相比简单hash 减少分布式缓存 添加一台机器或者减少一台机器时候,对于缓存路由到新的机子,导致缓存穿透的问题。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* 一致性hash算法
*/
public class ConsistenceHash {
/**
* 物理节点
*/
private List<String> realNode = new ArrayList<String>();
/**
* 物理节点对应的虚拟节点。key 对应的是物理节点,value对应的是虚拟节点的hash值
*/
private Map<String, List<Integer>> real2VirtualMap = new HashMap();
/**
* 虚拟节点数量
*/
private int virtualNum = 100;
/**
* 虚拟节点对应的物理节点,利用红黑树存贮
*/
private SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>();

/**
* 添加物理节点
*
* @param node
*/
public void addServer(String node) {
realNode.add(node);
String vnode = null;
int count = 0, i = 0;
while (count < virtualNum) {
vnode = node + "V_" + i;//生成虚拟节点
int hashValue = getHash(vnode); // 虚拟节点hash value
if (!sortedMap.containsKey(hashValue)) {
sortedMap.put(hashValue, node);
if (!real2VirtualMap.containsKey(node)) {
List<Integer> virtualNodes = new ArrayList<Integer>();
real2VirtualMap.put(node, virtualNodes);
}
real2VirtualMap.get(node).add(hashValue);
sortedMap.put(hashValue, node);
}
count++;
}
}

/**
* 删除物理节点,当节点下线或者说节点被移除
*
* @param node
*/
public void removeServer(String node) {
// 虚拟节点下园环
List<Integer> virtualNodes = real2VirtualMap.get(node);
if (virtualNodes != null && !virtualNodes.isEmpty()) {
for (Integer virtualNode : virtualNodes) {
sortedMap.remove(virtualNode);
}
}
//删除物理节点
realNode.remove(node);
virtualNodes.remove(node);

}


/**
* 通过 缓存key 获取物理节点
*
* @param key
* @return
*/
public String getServer(String key) {
int hashValue = getHash(key);
SortedMap<Integer, String> subMap = sortedMap.tailMap(hashValue);
//圆环当没有比这个hash更大的值的时候,获取最小值,最大值的总结是最小值
if (subMap == null || subMap.isEmpty()) {
return sortedMap.get(sortedMap.firstKey());
}
return subMap.get(subMap.firstKey());
}

/**
* 为什么不用 object的hash,因为 自带的hash 散列层度不够,而且有可能是负数
*
* @param str
* @return
*/
private static int getHash(String str) {
final int p = 1677619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++) {
hash = (hash ^ str.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
if (hash < 0) {
hash = Math.abs(hash);
}
return hash;
}


public static void main(String[] args){
ConsistenceHash consistenceHash=new ConsistenceHash();
consistenceHash.addServer("192.168.1.1");
consistenceHash.addServer("192.168.1.2");
consistenceHash.addServer("192.168.1.3");
consistenceHash.addServer("192.168.1.4");
consistenceHash.addServer("192.168.1.5");
System.out.println(consistenceHash.getServer("1"));
System.out.println(consistenceHash.getServer("2"));
System.out.println(consistenceHash.getServer("3"));
System.out.println(consistenceHash.getServer("4"));
System.out.println(consistenceHash.getServer("5"));
consistenceHash.removeServer("192.168.1.5");
System.out.println("after remove "+consistenceHash.getServer("1"));
System.out.println("after remove "+consistenceHash.getServer("2"));
System.out.println("after remove "+consistenceHash.getServer("3"));
System.out.println("after remove "+consistenceHash.getServer("4"));
System.out.println("after remove "+consistenceHash.getServer("5"));
}
}

参考资料