经缓存的数据大多数都不能够被命中,即数据无用。
3、一致哈希算法方式
何为一致哈希算法方式分布式呢?
想象一下,将32位的所有数字从小到大按顺时针分布在一个圆环上;
其次,将每个存储节点赋予一个名字,并通过crc32函数将其转换为32位的数字,此数字就是该memcached服务器的存储节点
接着,将key也通过crc32函数转换为32位的数字,它的所在位置按顺时针方向走第一个遇到的存储节点所对应的memcached服务器就是该key的最终存储服务器。
1)、图像解析
假设node1节点服务器挂了,根据按顺时针最近原则,那么原本存储在node1节点的数据此时也可存储在node3节点中。
假设有扩容的需要,增加的两台memcached服务器,又将会怎么样呢?请看下图分析
结果显示只有少量数据会受到影响,相对于整体数据来说这些影响还是在可接受的范围内。
从上面的图示我们可以很容易发现存在这么个缺点,即是使用crc32函数我们不能控制memcached存储节点的具体位置,并且节点的总数量相对于2的32次方是显得多么的渺小。假若恰好即使这几个存储节点都距离的非常近呢,那么必将有一个memcached服务器承受绝大多数的数据缓存。
请看下图分析:
解决办法:
将一个真实存储节点映射为多个虚拟存储节点,即真实节点+后缀再通过crc32处理(例如:node1_1、node1_2、node1_3、…..、node1_n)
看下图节点分布:
三个真实节点在圆环上就变成了三十个存储节点,这样就可以避免存储节点相距太近而导致数据缓存分布不均匀的问题了,而且存储机制没有任何变化。
2)、PHP代码实现
ConsistentHashMemcache.class.php
1 <?php
2 #分布式memcache 一致性哈希算法(采用环状数据结构)
3 class ConsistentHashMemcache
4 {
5 private $virtualNode=‘‘; #用于存储虚拟节点个数
6 private $realNode=array(); #用于存储真实节点
7 private $servers=array(); #用于存储memcache服务器信息
8 #private $totalNode=array(); #节点总数
9 /**
10 * @desc 构造函数
11 *
12 * @param $servers array | memcache服务器的信息
13 * @param $virtualNode int | 虚拟节点个数,默认64个
14 */
15 public function __construct($servers, $virtualNode=64)
16 {
17 $this->servers=$servers;
18 $this->realNode=array_keys($servers);
19 $this->virtualNode=$virtualNode;
20 }
21
22 /**
23 * @return int 返回32位的数字
24 */
25 private function hash($str)
26 {
27 return sprintf(‘%u‘,crc32($str)); #将字符串转换为32位的数字
28 }
29
30 /**
31 * @desc 处理节点
32 *
33 * @param $realNode array | 真实节点
34 * @param $virturalNode int | 虚拟节点个数
35 *
36 * @return array 返回所有节点信息
37 */
38 private function dealNode($realNode, $virtualNode)
39 {
40 $totalNode=array();
41 foreach ($realNode as $v)
42 {
43 for($i=0; $i<$virtualNode; $i++)
44 {
45 $hashNode=$this->hash($v.‘-‘.$i);
46 $totalNode[$hashNode]=$v;
47 }
48 }
49 ksort($totalNode); #按照索引进行排序,升序
50 return $totalNode;
51 }
52
53 /**
54 * @desc 获取key的真实存储节点
55 *
56 * @param $key string | key字符串
57 *
58 * @return string 返回真实节点
59 */
60 private function getNode($key)
61 {
62 $totalNode=$this->dealNode($this->realNode, $this->virtualNode); #获取所有虚拟节点
63 /* #查看虚拟节点总数
64 echo "<pre>";
65 print_r($totalNode);
66 echo "</pre>";die;
67 */
68 $hashNode=$this->hash($key); #key的哈希节点
69 foreach ($totalNode as $k => $v) #循环总结点环查找
70 {
71 if($k >= $hashNode) #查找第一个大于key哈希节点的值
72 {
73 return $v; #返回真实节点
74 }
75 }
76 return reset($totalNode); #假若总节点环的值都比key哈希节点小,则返回第一个总哈希环的value值
77 }
78
79 /**
80 * @desc 返回memcached对象
81 *
82 * @param $key string | key值
83 *
84 * @return object
85 */
86 private function getMemcached($key)
87 {
88 $node=$this->getNode($key); #获取真实节点
89 echo $key.‘真实节点:‘.$node.‘<br/>‘; #测试使用,查看key的真实节点
90 $host=$this->servers[$node][‘host‘]; #服务器池中某台服务器host
91 $port=$this->servers[$node][‘port‘]; #服务器池中某台服务器port
92 $m= new memcached(); #实例化
93 $m->addserver($host, $port); #添加memcache服务器
94 return $m; #返回memcached对象
95 }
96
97 /**
98 * @desc 设置key-value值
99 */
100 public function setKey($key, $value)
101 {
102 $m=$this->getMemcached($key);
103 return $m->set($key, $value);
104 }
105
106 /**
107 * @desc 获取key中的value
108 */
109 public function getKey($key)
110 {
111 $m=$this->getMemca