Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比
来源:互联网 发布:linux实时守护进程 编辑:程序博客网 时间:2024/04/26 02:45
准备工作:
① 配置文件 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 开始进行重新索引。
完整 哈希类:
1 <?php 2 //把字符串转换为整数 3 interface hash{ 4 public function _hash($str); 5 } 6 7 interface distribute{ 8 //在当前的服务器列表中找到合适的服务器存放数据 9 public function lookup($key); 10 11 //添加一个服务器到服务器列表中 12 public function addServer($server); 13 14 //从服务器列表中删除一个服务器 15 public function removeServer($server); 16 } 17 18 //一致性哈希 19 class consistentHash implements hash, distribute{ 20 21 private $serverList = array();//以二维数组保存服务器列表和每一个服务器下虚拟节点的哈希值 22 private $position = array();//以键值形式保存所有虚拟节点的哈希值(键)和对应的服务器(值)的一维数组 23 private $isSorted = FALSE; //记录虚拟节点哈希值列表是否已经排列过序 24 25 public function _hash($str){ 26 return sprintf('%u', crc32($str));//把字符串转成32为无符号整数 27 } 28 29 public function lookup($key){ 30 //计算出服务器的Hash值 31 $hash = $this->_hash($key); 32 33 //判断服务器列表是否排过序 34 if (!$this->isSorted) { 35 //倒序排列(把虚拟节点列表转换成逆时针圆环) 36 krsort($this->position, SORT_NUMERIC); 37 $this->isSorted = TRUE; 38 } 39 40 //遍历虚拟节点列表,找到合适的服务器并返回 41 foreach($this->position as $server_hash=> $server){ 42 if ($hash >= $server_hash) return $server; 43 } 44 return end($this->position); 45 } 46 47 public function addServer($server, $nodesNum = 25){ 48 49 if (isset($this->serverList[$server])) { 50 return; 51 } 52 53 //增加虚拟节点,默认每个物理节点变成25个虚拟节点 54 for($i = 0; $i < $nodesNum; $i++){ 55 $hash = $this->_hash($server.'-'.$i);//计算虚拟节点的Hash值 56 $this->position[$hash] = $server; 57 $this->serverList[$server][] = $hash; 58 } 59 60 //此时服务器列表发生了变化,因此标识为FALSE 61 $this->isSorted = FALSE; 62 return TRUE; 63 } 64 65 public function removeServer($server){ 66 67 if (!isset($this->serverList[$server])) { 68 return; 69 } 70 71 //循环position数组,如果要删除的服务器的值等于position数组某个元素的键,则删除该元素 72 foreach($this->position as $k=>$v){ 73 if($server == $v){ 74 unset($this->position[$k]); 75 } 76 } 77 78 unset($this->serverList[$server]); 79 80 $this->isSorted = FALSE; 81 return TRUE; 82 } 83 } 84 85 //普通哈希 86 class modHash implements hash, distribute { 87 private $serverList = array();//服务器列表 88 private $size = 0; //节点的个数 89 90 public function _hash($str){ 91 return sprintf('%u', crc32($str));//把字符串转成32为无符号整数 92 } 93 94 public function lookup($key){ 95 $key = $this->_hash($key) % $this->size; 96 return $this->serverList[$key]; 97 } 98 99 public function addServer($server){100 101 if (in_array($server, $this->serverList)) {102 return;103 }104 105 $this->serverList[] = $server;106 $this->size += 1;107 108 return true;109 }110 111 public function removeServer($server){112 113 if (!in_array($server, $this->serverList)) {114 return;115 }116 117 $key = array_search($server, $this->serverList);118 unset($this->serverList[$key]);119 $this->serverList = array_merge($this->serverList);//删除节点后重新索引数组120 $this->size -= 1;121 122 return true;123 }124 }
测试普通哈希类节点是否正确:
<?phprequire './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:11212key2 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
<?phpheader("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
<?phpheader("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
<?phpheader("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() 输出数据格式如下
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 的普通哈希分布算法和一致性哈希分布算法命中率对比
- Memcached 笔记与总结(6)PHP 实现 Memcached 的一致性哈希分布算法
- 【总结】一致性哈希算法(Memcached)
- memcached:一致性哈希算法
- memcached一致性哈希算法
- 分布算法之一致性哈希算法
- memcached分布测试报告(一致性哈希情况下的散列函数选择)
- memcached分布测试报告(一致性哈希情况下的散列函数选择)
- memcached客户端(分布算法)
- 哈希算法分布与一致性哈希算法详解
- memcached > 分布算法
- memcached分布测试报告(一致性哈…
- 哈希分布与一致性哈希算法简介
- 哈希分布与一致性哈希算法简介
- 哈希分布与一致性哈希算法简介
- 哈希分布与一致性哈希算法简介
- 哈希分布与一致性哈希算法简介
- 哈希分布与一致性哈希算法简介(1)
- 关于virtualbox虚拟机恢复备份rails出错
- Scala-Spark实现RF(随机森林)
- ubuntu 下安裝pycharm
- php遇到的CURL问题
- openjudge每日推荐——7624山区建小学
- Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比
- leetcode_83. Remove Duplicates from Sorted List 删除单链表中的重复节点
- jquery validate 动态控制 错误信息(根据ajax 返回信息)
- 利用ITEXT操作PDF
- dynatrace 性能监测
- Flash检尸倒计时,Google和微软继续加强浏览器HTML5的功能
- 总结
- 作为一个iOS攻城狮不得不了解的网络知识
- Java工具方法总结(一)