今天写点周末在火车上看的memcached的东西:
一:memcached的分布式
虽然memcached被称为“分布式”缓存服务器,但是服务器端并没有“分布式”的功能。而是通过客户端来实现的。
Memcached分布式原理:
假设有5台memcached服务器:node1,node2… node5。现在要保存键为key1,key2…key10的数据。首先往memcached中添加key1。将key1传给客户端程序之后,客户端实现的算法会根据这个键“key1”来决定保存数据的memcached服务器。
将服务器选定之后,将会用选定的服务器来保存“key1”和对应的值。
在获取数据的时候,通过先根据要获取的数据的key来根据客户端实现的相同的算法选择对应的数据保存的服务器,然后取出数据。
这样就实现了memcached的分布式。Memcached的服务器增多,则键就会更加的分散。及时一台服务器挂掉,也不会影响其他的缓存。
Memcached分布式方法:
1.根据余数计算
这种方法简单的说就是”根据服务器的台数的余数来进行分散“。首先求取键所对应的整数哈希值,然后根据余数来选择服务器。
这种方法简单高效,而且数据的分散性也非常的好。但是问题是当增加或者删除一台memcached服务器的时候,余数就会发生巨大的变化。这样就没有办法获取和保存时间相应的服务器。从而会极大的降低缓存的命中率。2
2. 一致性哈希
这种方法首先求出memcached服务器的哈希值,然后将它分配到0~2^32的圆上,然后使用同样的办法求出数据的健的哈希值,将其映射到圆上。然后从数据映射的点开始顺时针的查找,将数据保存到查找到的第一台服务器上面。如果超出了2^32仍然没有找到服务器,那么就将数据保存到第一台memcached服务器上面。
这种方法在一定程度上决了在修改memcached服务器数据的时候对缓存命中率的影响。在一致性哈希算法中,只有在这个圆上,从增加服务器的那个点逆时针遇到的第一台服务器之间的健会受到影响。因此一致性哈希最大限度的抑制了键的重新分布。
另外一些一致性哈希算法也采用了虚拟节点的办法。因为使用一般的hash函数的话,服务器的映射地点会分布的可能不太均匀,因此使用虚拟节点的思想,为每一台服务器在圆上分配100~300个点。这样就能够抑制分布不均匀,最大限度的减少服务器增加或者减少的时候缓存的重新分布。
参考代码:
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
|
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
;
}
int
hash = hashFunction.hash(key);
if
(!circle.containsKey(hash)) {
SortedMap<Integer, T> tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
return
circle.get(hash);
}
}
|
参考资料:
1. 一个java版本的一致性哈希实例:https://weblogs.java.net/blog/2007/11/27/consistent-hashing
2. 一致性哈希:http://zh.wikipedia.org/wiki/%E4%B8%80%E8%87%B4%E5%93%88%E5%B8%8C