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

准备工作

① 配置文件 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 }
View Code

 

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

<?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 服务器:

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第1张图片

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

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第3张图片

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第4张图片

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第5张图片

 

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

 Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第6张图片

输出:初始化数据完毕

 

 

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

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第7张图片

 

(127.0.0.1:11211)输入 stats:

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第8张图片

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第9张图片

其中 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
)
View Code

 

 

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%:

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第10张图片

 

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

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第11张图片

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

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第12张图片

 

 Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第13张图片

 Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第14张图片

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

 

 

一致性哈希

修改 init.php 和 down.php:

$hash = new consistentHash();

 

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

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第15张图片 

 

 Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第16张图片

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

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第17张图片

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第18张图片

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第19张图片 

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第20张图片

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

 

Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比_第21张图片

 

 

 

 

你可能感兴趣的:(Memcached 笔记与总结(8)Memcached 的普通哈希分布算法和一致性哈希分布算法命中率对比)