仅仅把 Memcached 服务器集群地址通过一致性哈希转映射在圆环上,可能会出现数据不能均匀地分配给各台 Memcached 服务器。

解决方案是引入虚拟节点,就是把每个映射在圆环上的服务器地址(物理节点)转变成更多的(注:关于虚拟节点的个数参考)虚拟节点。

修改 Memcached 笔记与总结(6)PHP 实现 Memcached 的一致性哈希分布算法 中的代码:

类 consistentHash 增加私有的成员属性:$position,以键值形式保存所有虚拟节点的哈希值(键)和对应的服务器(值)的一维数组

结构如下:

array(
1589449412 => '192.168.1.1'
2566189294 => '192.168.1.1'
4025729144 => '192.168.1.2'
2135743977 => '192.168.1.2'
139193727 => '192.168.1.3'
1522140696 => '192.168.1.3'
)

私有成员属性 $serverList 修改为 以二维数组保存服务器列表和每一个服务器下虚拟节点的哈希值,结构如下:

array(
'192.168.1.1' => array(
0 => 1589449412,
1 => 700064338
),
'192.168.1.2' => array(
0 => 1559997597,
1 => 737975307
)
)

私有成员属性 isSorted 修改为 记录虚拟节点哈希值列表是否已经排列过序

addServer 方法的实现:

    public function addServer($server, $nodesNum = 25){

        if (isset($this->serverList[$server])) {
return;
} //增加虚拟节点,默认每个物理节点变成25个虚拟节点
for($i = 0; $i < $nodesNum; $i++){
$hash = $this->_hash($server.'-'.$i);//计算虚拟节点的Hash值
$this->position[$hash] = $server;
$this->serverList[$server][] = $hash;
} //此时虚拟节点表发生了变化,因此标识为FALSE
$this->isSorted = FALSE;
return TRUE;
}

removeServer 方法的实现:

    public function removeServer($server){

        if (!isset($this->serverList[$server])) {
return;
} //循环position数组,如果要删除的服务器的值等于position数组某个元素的值,则删除该元素
foreach($this->position as $k=>$v){
if($server == $v){
unset($this->position[$k]);
}
} unset($this->serverList[$server]); $this->isSorted = FALSE;
return TRUE;
}

lookup 方法的实现:

    public function lookup($key){
//计算出服务器的Hash值
$hash = $this->_hash($key); //判断服务器列表是否排过序
if (!$this->isSorted) {
//倒序排列(把虚拟节点列表转换成逆时针圆环)
krsort($this->position, SORT_NUMERIC);
$this->isSorted = TRUE;
} //遍历虚拟节点列表,找到合适的服务器并返回
foreach($this->position as $server_hash=> $server){
if ($hash >= $server_hash) return $server;
}
return end($this->position);
}

完整代码:

<?php
//把字符串转换为整数
interface hash{
public function _hash($str);
} interface distribute{
//在当前的服务器列表中找到合适的服务器存放数据
public function lookup($key); //添加一个服务器到服务器列表中
public function addServer($server); //从服务器列表中删除一个服务器
public function removeServer($server);
} class consistentHash implements hash, distribute{ private $serverList = array();//以二维数组保存服务器列表和每一个服务器下虚拟节点的哈希值
private $position = array();//以键值形式保存所有虚拟节点的哈希值(键)和对应的服务器(值)的一维数组
private $isSorted = FALSE; //记录虚拟节点哈希值列表是否已经排列过序 public function _hash($str){
return sprintf('%u', crc32($str));//把字符串转成32为无符号整数
} public function lookup($key){
//计算出服务器的Hash值
$hash = $this->_hash($key); //判断服务器列表是否排过序
if (!$this->isSorted) {
//倒序排列(把虚拟节点列表转换成逆时针圆环)
krsort($this->position, SORT_NUMERIC);
$this->isSorted = TRUE;
} //遍历虚拟节点列表,找到合适的服务器并返回
foreach($this->position as $server_hash=> $server){
if ($hash >= $server_hash) return $server;
}
return end($this->position);
} public function addServer($server, $nodesNum = 25){ if (isset($this->serverList[$server])) {
return;
} //增加虚拟节点,默认每个物理节点变成25个虚拟节点
for($i = 0; $i < $nodesNum; $i++){
$hash = $this->_hash($server.'-'.$i);//计算虚拟节点的Hash值
$this->position[$hash] = $server;
$this->serverList[$server][] = $hash;
} //此时虚拟节点列表发生了变化,因此标识为FALSE
$this->isSorted = FALSE;
return TRUE;
} public function removeServer($server){ if (!isset($this->serverList[$server])) {
return;
} //循环position数组,如果要删除的服务器的值等于position数组某个元素的值,则删除该元素
foreach($this->position as $k=>$v){
if($server == $v){
unset($this->position[$k]);
}
} unset($this->serverList[$server]); $this->isSorted = FALSE;
return TRUE;
}
} $hashserver = new consistentHash(); $hashserver->addServer('192.168.1.1');
$hashserver->addServer('192.168.1.2');
$hashserver->addServer('192.168.1.3');
$hashserver->addServer('192.168.1.4');
$hashserver->addServer('192.168.1.5'); echo 'save key1 on server:',$hashserver->lookup('key1'),'<br />';
echo 'save key2 on server:',$hashserver->lookup('key2'),'<br /><br />'; $hashserver->removeServer('192.168.1.2');
echo 'save key1 on server:',$hashserver->lookup('key1'),'<br />';
echo 'save key2 on server:',$hashserver->lookup('key2'),'<br /><br />'; $hashserver->addServer('192.168.1.6');
echo 'save key1 on server:',$hashserver->lookup('key1'),'<br />';
echo 'save key2 on server:',$hashserver->lookup('key2'),'<br /><br />';

输出:

save key1 on server:192.168.1.2
save key2 on server:192.168.1.1 save key1 on server:192.168.1.3
save key2 on server:192.168.1.1 save key1 on server:192.168.1.6
save key2 on server:192.168.1.1

参考:

Memcached中一致性哈希(Consistent Hashing)的运用

memcached 之 哈希一致性 和 虚拟节点 分析

Memcached 笔记与总结(7)增加虚拟节点的更多相关文章

  1. memcached学习——分布式算法(Consistant hash + 虚拟节点)(三)

    1.取余算法 优点:数据分布均匀缺点:当服务器动态的添加.删除节点或者某台server down掉,会导致命中率超大幅度下降,甚至导致服务不可用 2.Consistant Hash算法:一致性哈希算法 ...

  2. Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比

    准备工作: ① 配置文件 config.php ② 封装 Memcached 类 hash.class.php,包含普通哈希算法(取模)和一致性哈希算法 ③ 初始化 Memcached 节点信息 in ...

  3. Memcached笔记之分布式算法

    1.根据余数进行分散:离散度高,但是增加或者移除服务器的时候,缓存充足的代价非常大.添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而音像缓存的命中率. 2.Consistent ...

  4. Hadoop学习笔记—13.分布式集群中节点的动态添加与下架

    开篇:在本笔记系列的第一篇中,我们介绍了如何搭建伪分布与分布模式的Hadoop集群.现在,我们来了解一下在一个Hadoop分布式集群中,如何动态(不关机且正在运行的情况下)地添加一个Hadoop节点与 ...

  5. Memcached笔记——(四)应对高并发攻击【转】

    http://snowolf.iteye.com/blog/1677495 近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最 ...

  6. 快速增加controller节点

    # controller1节点部署成功后,再添加controller节点,复制配置文件并修改即可openstack pike 部署 目录汇总 http://www.cnblogs.com/elvi/p ...

  7. ACK容器服务虚拟节点使用阿里云日志服务来收集业务容器日志

    按照这篇博文的介绍,可以在ACK集群上通过Helm的方式部署虚拟节点,提升集群的弹性能力.现在,通过虚拟节点部署的ECI弹性容器实例也支持将stdout输出.日志文件同步到阿里云日志服务(SLS)进行 ...

  8. Memcached笔记——(四)应对高并发攻击

    近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行Mis ...

  9. Memcached笔记——(二)XMemcached&Spring集成

    今天研究Memcached的Java的Client,使用XMemcached 1.3.5,做个简单的测试,并介绍如何与Spring集成. 相关链接: Memcached笔记--(一)安装&常规 ...

随机推荐

  1. configSections

         由于最近一个项目的数据库变动比较频繁, 为了减少数据层的负担, 打算采用.net的MVC框架, 使用LINQ对付数据层.       这个框架的web.config文件里出现了configS ...

  2. JavaScrip实现3D旋转动态效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 递推水题 Codeforces Round #289 (Div. 2, ACM ICPC Rules) A. Maximum in Table

    题目传送门 /* 模拟递推水题 */ #include <cstdio> #include <iostream> #include <cmath> #include ...

  4. NoSQL-Redis【1】-控制台配置密码

    1.设置密码为123456 CONFIG SET requirepass 123456 2.验证密码 AUTH 123456 3.redis-cli连接 @ECHO OFF redis-cli.exe ...

  5. POJ3308 Paratroopers(最小割/二分图最小点权覆盖)

    把入侵者看作边,每一行每一列都是点,选取某一行某一列都有费用,这样问题就是选总权最小的点集覆盖所有边,就是最小点权覆盖. 此外,题目的总花费是所有费用的乘积,这时有个技巧,就是取对数,把乘法变为加法运 ...

  6. .net平台下C#socket通信(上)

    在开始介绍socket前先补充补充基础知识,在此基础上理解网络通信才会顺理成章,当然有基础的可以跳过去了.都是废话,进入正题. TCP/IP:Transmission Control Protocol ...

  7. Hive内部表外部表转化分析(装)

    link:http://anyoneking.com/archives/127hive表分为内部表和外部表.外部表在删除的时候并不会删除到hdfs中的文件,比较安全,所以对于重要的需要进行分析的日志建 ...

  8. 51Nod 1046 A^B Mod C Label:快速幂

    给出3个正整数A B C,求A^B Mod C.   例如,3 5 8,3^5 Mod 8 = 3. Input 3个正整数A B C,中间用空格分隔.(1 <= A,B,C <= 10^ ...

  9. ld: warning: directory not found for option 去掉警告的方法

    工程老是提示ld: warning: directory not found for option:xxxxxx 这种提示,通常是由于添加了第三方SDK,但是后来改了个名字或者去掉了SDK,但是在 B ...

  10. python 之Twsited

    Twisted是一个事件驱动的网络框架,其中包含了诸多功能,例如网络协议,线程,数据库管理,网络操作,电子邮件等 事件驱动 一,注册事件 二,触发事件 自定义事件框架  event_fram.py # ...