准备工作

① 配置文件 config.php

② 封装 Memcached 类 hash.class.php,包含普通哈希算法(取模)和一致性哈希算法

③ 初始化 Memcached 节点信息 init.php

④ 减少 Memcached 节点 down.php

⑤ 统计命中率 statistics.php

⑥ 使用 Highcharts(4.1.9) js 图表库来展示减少节点后两种算法命中率的变化

1. 配置文件

config.php

<?php
/*
Memcached 配置文件
*/ //Memcached 节点信息
$mem_servers = array();
$mem_servers['s1'] = array('host'=>'127.0.0.1', 'port'=>'11211');
$mem_servers['s2'] = array('host'=>'127.0.0.1', 'port'=>'11212');
$mem_servers['s3'] = array('host'=>'127.0.0.1', 'port'=>'11213');
$mem_servers['s4'] = array('host'=>'127.0.0.1', 'port'=>'11214');
$mem_servers['s5'] = array('host'=>'127.0.0.1', 'port'=>'11215'); //哈希策略选择
$method = 'mod';//普通哈希
//$method = 'dis';//一致性哈希

说明:模拟 5 台 Memcached 服务器,使用相同的本地主机,不同的端口号。

2. 封装 Memcached 类

在 Memcached 笔记与总结(6)PHP 实现 Memcached 的一致性哈希分布算法 的基础上增加普通哈希类:

//普通哈希
class modHash implements hash, distribute {
private $serverList = array();//服务器列表
private $size = 0; //节点的个数 public function _hash($str){
return sprintf('%u', crc32($str));//把字符串转成32为无符号整数
} public function lookup($key){
$key = $this->_hash($key) % $this->size; //取模
return $this->serverList[$key];
} public function addServer($server){ if (in_array($server, $this->serverList)) {
return;
} $this->serverList[] = $server;
$this->size += 1; return true;
} public function removeServer($server){ if (!in_array($server, $this->serverList)) {
return;
} $key = array_search($server, $this->serverList);
unset($this->serverList[$key]);
$this->serverList = array_merge($this->serverList);//删除节点后重新索引数组
$this->size -= 1; return true;
}
}

说明:如果仅仅向 array_merge() 函数输入了一个数组,且键名是整数,则该函数将返回带有整数键名的新数组,其键名以 0 开始进行重新索引。

完整 哈希类:

 <?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;
}
} //普通哈希
class modHash implements hash, distribute {
private $serverList = array();//服务器列表
private $size = 0; //节点的个数 public function _hash($str){
return sprintf('%u', crc32($str));//把字符串转成32为无符号整数
} public function lookup($key){
$key = $this->_hash($key) % $this->size;
return $this->serverList[$key];
} public function addServer($server){ if (in_array($server, $this->serverList)) {
return;
} $this->serverList[] = $server;
$this->size += 1; return true;
} public function removeServer($server){ if (!in_array($server, $this->serverList)) {
return;
} $key = array_search($server, $this->serverList);
unset($this->serverList[$key]);
$this->serverList = array_merge($this->serverList);//删除节点后重新索引数组
$this->size -= 1; return true;
}
}

测试普通哈希类节点是否正确:

<?php
require './config.php';
require './hash.class.php'; $hashserver = new modHash(); $hashserver->addServer($mem_servers['s1']);
$hashserver->addServer($mem_servers['s2']);
$hashserver->addServer($mem_servers['s3']);
$hashserver->addServer($mem_servers['s4']);
$hashserver->addServer($mem_servers['s5']); function showServer($obj, $key) {
$serverInfo = $obj->lookup($key);
return $key.' on server:'.$serverInfo['host'].", port:".$serverInfo['port'];
} echo showServer($hashserver, 'key1'),'<br />';
echo showServer($hashserver, 'key2'),'<br />';

输出:

key1 on server:127.0.0.1, port:11212
key2 on server:127.0.0.1, port:11213

其中 key1 经过 crc32 转换后得到 744252496,模 5 为 1;key2 经过 crc32 转换后得到 3042260458,模 5 为 3。

3. 初始化 Memcached 节点信息 init.php

循环添加服务器,并且把 10000 条数据(按照普通哈希/一致性哈希)插入到添加的 5 台 Memcached 服务器中,平均每台 2000 条数据

分别开启 5 台 Memcached 服务器:

init.php

<?php
header("Content-type:text/html; charset=utf-8"); set_time_limit(0); require './config.php';
require './hash.class.php'; $mem = new memcache();
$hash = new modHash();//普通哈希 //循环添加服务器
foreach($mem_servers as $k=>$v){
$hash->addServer($k);
} //向服务器中添加共10000条数据
for($i = 0; $i < 10000; $i++) {
$key = 'key'.$i;
$value = 'value'.$i;
$server = $mem_servers[$hash->lookup($key)];
$mem->pconnect($server['host'], (int)$server['port'], 2);//设置超时时间为2秒
$mem->set($key, $value, 0, 0);//不自动过期
usleep(3000);
} echo '初始化数据完毕';

说明:

memcache::pconnect() :打开一个到服务器的持久化连接,它的第 2 个参数要求是长整型 long

执行 init.php

输出:初始化数据完毕

使用 Telnet 客户端连接 Memcached 服务器查看数据:

(127.0.0.1:11211)输入 stats:

其中 total_items 有 2559 个。

4. 减少 Memcached 节点 :down.php 

<?php
header("Content-type:text/html; charset=utf-8"); set_time_limit(0); require './config.php';
require './hash.class.php'; $mem = new memcache();
$hash = new modHash();//普通哈希 //循环添加服务器
foreach($mem_servers as $k=>$v){
$hash->addServer($k);
} //模拟减少一台Memcached服务器
$hash->removeServer('s3'); for($i = 0; $i < 10000; $i++) {
$key = 'key'.$i;
$value = 'value'.$i;
$server = $mem_servers[$hash->lookup($key)];
$mem->pconnect($server['host'], (int)$server['port'], 2);//设置超时时间为2秒
if(!$mem->get($key, $value)){
$mem->set($key, $value, 0, 0);//不自动过期
}
usleep(3000);
}

5. 统计命中率 statistics.php

统计 Memcached 各节点的平均命中率,用于 Ajax 请求

statistics.php

<?php
header("Content-type:text/html; charset=utf-8"); set_time_limit(0); require './config.php'; $mem = new memcache();
$gets = 0;//请求次数
$hits = 0;//命中次数 foreach ($mem_servers as $k => $v) {
$mem->pconnect($v['host'], $v['port'], 2);//设置超时时间为2秒
$res = $mem->getstats(); $gets += $res['cmd_get'];
$hits += $res['get_hits'];
} $rate = 1;
if($gets > 0) {
$rate = $hits / $gets;
} echo $rate;

说明:

memcache::getstats() 输出数据格式如下

Array
(
[pid] => 9616
[uptime] => 2742
[time] => 1448207741
[version] => 1.4.24
[libevent] => 2.0.22-stable
[pointer_size] => 32
[rusage_user] => 2.168000
[rusage_system] => 8.439000
[curr_connections] => 12
[total_connections] => 13
[connection_structures] => 13
[reserved_fds] => 20
[cmd_get] => 4
[cmd_set] => 7045
[cmd_flush] => 0
[cmd_touch] => 0
[get_hits] => 2
[get_misses] => 2
[delete_misses] => 0
[delete_hits] => 0
[incr_misses] => 0
[incr_hits] => 0
[decr_misses] => 0
[decr_hits] => 0
[cas_misses] => 0
[cas_hits] => 0
[cas_badval] => 0
[touch_hits] => 0
[touch_misses] => 0
[auth_cmds] => 0
[auth_errors] => 0
[bytes_read] => 195866
[bytes_written] => 89803
[limit_maxbytes] => 4194304
[accepting_conns] => 1
[listen_disabled_num] => 0
[threads] => 4
[conn_yields] => 0
[hash_power_level] => 16
[hash_bytes] => 262144
[hash_is_expanding] => 0
[malloc_fails] => 0
[bytes] => 62780
[curr_items] => 1000
[total_items] => 1000
[expired_unfetched] => 0
[evicted_unfetched] => 0
[evictions] => 0
[reclaimed] => 0
[crawler_reclaimed] => 0
[crawler_items_checked] => 0
[lrutail_reflocked] => 0
)

6. 使用 Highcharts(4.1.9) js 图表库来展示减少节点后两种算法命中率的变化

官方地址:http://www.highcharts.com/

下载地址:http://code.highcharts.com/zips/Highcharts-4.1.9.zip

index.html,该文件通过 Ajax 每 2 秒向 statistics.php 发出请求获取 Memcached 的命中率

注:解压 Highcharts 压缩包, 拷贝 Highcharts-4.1.9\examples\dynamic-update\index.html 至项目目录,修改并且重命名为 index.html

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Highcharts Example</title> <script type="text/javascript" src="jquery-1.8.3.min.js"></script>
<style type="text/css">
${demo.css}
</style>
<script type="text/javascript">
$(function () {
$(document).ready(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
}); $('#container').highcharts({
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () { // set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = parseFloat($.ajax({url:'statistics.php', async:false}).responseText);
series.addPoint([x, y], true, true);
}, 2000);
}
}
},
title: {
text: 'Memcached hit rates'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i; for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: 1
});
}
return data;
}())
}]
});
});
});
</script>
</head>
<body>
<script src="./Highcharts-4.1.9/js/highcharts.js"></script>
<script src="./Highcharts-4.1.9/js/modules/exporting.js"></script> <div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div> </body>
</html>

比较过程

当没有减少节点时,访问 index.html 时,命中保持在 100%:

当减少一个服务器节点时,即执行 down.php,命中率的变化(普通哈希)变化如下:

00:46:45 时突然减少一台服务器,命中率急剧下降;

直到 01:00:10 时恢复稳定。耗时约 13 min,稳定后的命中率在 94% - 95% 之间。

一致性哈希

修改 init.php 和 down.php:

$hash = new consistentHash();

首先执行 init.php,然后当减少一个服务器节点时(执行 down.php),一致性哈希命中率的变化变化如下:

01:32:39 模拟宕调一台服务器

到 01:45:55 恢复稳定。耗时约 13 min,稳定后的命中率为 95.03%。

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比的更多相关文章

  1. Memcached笔记——(三)Memcached使用总结

    为了将N个前端数据同步,通过Memcached完成数据打通,但带来了一些新问题: 使用iBatis整合了Memcached,iBatis针对每台server生成了唯一标识,导致同一份数据sql会产生不 ...

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

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

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

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

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

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

  5. Memcached笔记——(一)安装&常规错误&监控

    08年的时候接触过Memcached,当时还对它的客户端产品嗤之以鼻,毕竟手工代码没有各种ORM原生XML配置方便.尽管如此,Memcached现在已经成了服务器架构里不可或缺的一部分! 相关链接: ...

  6. Linux实战教学笔记32:企业级Memcached服务应用实践

    一, Memcached介绍 1.1 Memcached与常见同类软件对比 (1)Memcached是什么? Memcached是一个开源的,支持高性能,高并发的分布式内存缓存系统,由C语言编写,总共 ...

  7. Memcached 笔记与总结(7)增加虚拟节点

    仅仅把 Memcached 服务器集群地址通过一致性哈希转映射在圆环上,可能会出现数据不能均匀地分配给各台 Memcached 服务器. 解决方案是引入虚拟节点,就是把每个映射在圆环上的服务器地址(物 ...

  8. Memcached 笔记与总结(9)Memcached 与 Session

    一.Memcached 存储 Session 由于 Memcached 是分布式的内存对象缓存系统,因此可以用来实现 Session 同步:把 Web 服务器中的内存组合起来,成为一个“内存池”,不管 ...

  9. Memcached 笔记与总结(5)Memcached 的普通哈希分布和一致性哈希分布

    普通 Hash 分布算法的 PHP 实现 首先假设有 2 台服务器:127.0.0.1:11211 和 192.168.186.129:11211 当存储的 key 经过对 2 (2 台服务器)取模运 ...

随机推荐

  1. 思维题(转换) HDU 4370 0 or 1

    题目传送门 题意:题目巨晦涩的传递出1点和n点的初度等于入度等于1, 其余点出度和入度相等 分析:求最小和可以转换成求最短路,这样符合条件,但是还有一种情况.1点形成一个环,n点也形成一个环,这样也是 ...

  2. Service Provider模式

    参考文章:[http://blog.csdn.net/zl3450341/article/details/7227197] Service Interface:服务接口,将服务通过抽象统一声明,供客户 ...

  3. 由case 和 break 引发的思考

    我在使用case 语句的时候,发现结果不对劲,原来我是忽略了break这个语句的添加 然后,我要反思的是,这样基本的语句,我这样的错误也能犯,证明我自己之前还没有真正掌握这个语句的使用. 所以,真正地 ...

  4. java语言特性概述

    一.前言 我们都知道java是面向对象的编程,其中四个基本特性:抽象.封装.继承.多态.这四个特性,概括起来可以这么理解,抽象.封装.继承是多态的基础,多态是抽象.封装.继承的表现. 二. JAVA ...

  5. topcoder SRM 625 DIV2 IncrementingSequence

    由于题目数据量比较小,故可以开辟一个数组存储每个index出现的次数 然后遍历即可 string canItBeDone(int k, vector<int> A){ vector< ...

  6. 链式前向星+SPFA

    今天听说vector不开o2是数组时间复杂度常数的1.5倍,瞬间吓傻.然后就问好的图表达方式,然后看到了链式前向星.于是就写了一段链式前向星+SPFA的,和普通的vector+SPFA的对拍了下,速度 ...

  7. SRM 595 DIV2 1000

    数位DP的感觉,但是跟模版不是一个套路的,看的题解,代码好理解,但是确实难想. #include <cstdio> #include <cstring> #include &l ...

  8. NOI模拟赛Day2

    深深的感受到了自己的水 ---------------------------------------------------------------------------------------- ...

  9. 通过Ajax post Json类型的数据到Controller

    View function postSimpleData() { $.ajax({ type: "POST", url: "/Service/SimpleData&quo ...

  10. xml学习

    一,数据类型 xmlChar  对char的基本代替,是一个UTF-8编码字符串中的一个字节.如果你的数据使用了其他编码,在使用libxml函数前就必须转换为UTF-8. xmlDoc和xmlDocP ...