memcache一致性hash的php实现方法


Posted in PHP onMarch 05, 2015

本文实例讲述了memcache一致性hash的php实现方法。分享给大家供大家参考。具体如下:

最近在看一些分布式方面的文章,所以就用php实现一致性hash来练练手,以前一般用的是最原始的hash取模做 分布式,当生产过程中添加或删除一台memcache都会造成数据的全部失效,一致性hash就是为了解决这个问题,把失效数据降到最低,相关资料可以 google一下!

php实现效率有一定的缺失,如果要高效率,还是写扩展比较好

经测试,5个memcache,每个memcache生成100个虚拟节点,set加get1000次,与单个memcache直接set加get慢5倍,所以效率一般,有待优化!

在阅读本文之前,最好知道二分查找法。

实现过程:

memcache的配置 ip+端口+虚拟节点序列号 做hash,使用的是crc32,形成一个闭环。
对要操作的key进行crc32
二分法在虚拟节点环中查找最近的一个虚拟节点
从虚拟节点中提取真实的memcache ip和端口,做单例连接

<?php

class memcacheHashMap {

        private $_node = array();

        private $_nodeData = array();

        private $_keyNode = 0;

        private $_memcache = null;

        //每个物理服务器生成虚拟节点个数 [注:节点数越多,cache分布的均匀性越好,同时set get操作时,也更耗资源,10台物理服务器,采用200较为合理]

        private $_virtualNodeNum = 200;

        private function __construct() {

                $config = array(//五个memcache服务器

                                                '127.0.0.1:11211',

                                                '127.0.0.1:11212',

                                                '127.0.0.1:11213',

                                                '127.0.0.1:11214',

                                                '127.0.0.1:11215'

                                        );

                if (!$config) throw new Exception('Cache config NULL');

                foreach ($config as $key => $value) {

                        for ($i = 0; $i < $this->_virtualNodeNum; $i++) {

                                $this->_node[sprintf("%u", crc32($value . '_' . $i))] = $value . '_' . $i;//循环为每个memcache服务器创建200个虚拟节点

                        }

                }

                ksort($this->_node);//创建出来的1000个虚拟节点按照键名从小到大排序

        }

        //实例化该类

        static public function getInstance() {

                static $memcacheObj = null;

                if (!is_object($memcacheObj)) {

                        $memcacheObj = new self();

                }

                return $memcacheObj;

        }

        //根据传来的键查找到对应虚拟节点的位置

        private function _connectMemcache($key) {

                $this->_nodeData = array_keys($this->_node);//所有的虚拟节点的键的数组

                $this->_keyNode = sprintf("%u", crc32($key));//算出键的hash值

                $nodeKey = $this->_findServerNode();//找出对应的虚拟节点

                //如果超出环,从头再用二分法查找一个最近的,然后环的头尾做判断,取最接近的节点

                if ($this->_keyNode > end($this->_nodeData)) {

                        $this->_keyNode -= end($this->_nodeData);

                        $nodeKey2 = $this->_findServerNode();

                        if (abs($nodeKey2 - $this->_keyNode) < abs($nodeKey - $this->_keyNode))  $nodeKey = $nodeKey2;

                }

                var_dump($this->_node[$nodeKey]);

                list($config, $num) = explode('_', $this->_node[$nodeKey]);

                if (!$config) throw new Exception('Cache config Error');

                if (!isset($this->_memcache[$config])) {

                        $this->_memcache[$config] = new Memcache;

                        list($host, $port) = explode(':', $config);

                        $this->_memcache[$config]->connect($host, $port);

                }

                return $this->_memcache[$config];

        }

        //二分法根据给出的值找出最近的虚拟节点位置

        private function _findServerNode($m = 0, $b = 0) {

            $total = count($this->_nodeData);

            if ($total != 0 && $b == 0) $b = $total - 1;

            if ($m < $b){

                $avg = intval(($m+$b) / 2);

                if ($this->_nodeData[$avg] == $this->_keyNode) return $this->_nodeData[$avg];

                elseif ($this->_keyNode < $this->_nodeData[$avg] && ($avg-1 >= 0)) return $this->_findServerNode($m, $avg-1);

                else return $this->_findServerNode($avg+1, $b);

            }

                if (abs($this->_nodeData[$b] - $this->_keyNode) < abs($this->_nodeData[$m] - $this->_keyNode))  return $this->_nodeData[$b];

                else return $this->_nodeData[$m];

        }

        public function set($key, $value, $expire = 0) {

                return $this->_connectMemcache($key)->set($key, json_encode($value), 0, $expire);

        }

        public function add($key, $value, $expire = 0) {

                return $this->_connectMemcache($key)->add($key, json_encode($value), 0, $expire);

        }

        public function get($key) {

                return json_decode($this->_connectMemcache($key)->get($key), true);

        }

        public function delete($key) {

                return $this->_connectMemcache($key)->delete($key);

        }

}

$runData['BEGIN_TIME'] = microtime(true);

//测试一万次set加get

for($i=0;$i<10000;$i++) {

        $key = md5(mt_rand());

        $b = memcacheHashMap::getInstance()->set($key, time(), 10);

}

var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6));

$runData['BEGIN_TIME'] = microtime(true); $m= new Memcache;

$m->connect('127.0.0.1', 11211);

for($i=0;$i<10000;$i++) {

        $key = md5(mt_rand());

        $b = $m->set($key, time(), 0, 10);

}

var_dump(number_format(microtime(true) - $runData['BEGIN_TIME'],6));

?>

希望本文所述对大家的php程序设计有所帮助。

PHP 相关文章推荐
PHP生成静态页
Nov 25 PHP
php 分页类 扩展代码
Jun 11 PHP
php xml留言板 xml存储数据的简单例子
Aug 24 PHP
WIN8.1下搭建PHP5.6环境
Apr 29 PHP
php邮件发送的两种方式
Apr 28 PHP
CodeIgniter连贯操作的底层原理分析
May 17 PHP
php面向对象之反射功能与用法分析
Mar 29 PHP
Thinkphp 空操作、空控制器、命名空间(详解)
May 05 PHP
Ubuntu上安装yaf扩展的方法
Jan 29 PHP
php和asp语法上的区别总结
May 12 PHP
PHP数组与字符串互相转换实例
May 05 PHP
使用Rancher在K8S上部署高性能PHP应用程序的教程
Jul 10 PHP
PHP将session信息存储到数据库的类实例
Mar 04 #PHP
php微信支付之APP支付方法
Mar 04 #PHP
php支付宝手机网页支付类实例
Mar 04 #PHP
php银联网页支付实现方法
Mar 04 #PHP
php随机抽奖实例分析
Mar 04 #PHP
php二维数组合并及去重复的方法
Mar 04 #PHP
php中get_cfg_var()和ini_get()的用法及区别
Mar 04 #PHP
You might like
php将图片保存入mysql数据库失败的解决方法
2014/12/27 PHP
Yii学习总结之数据访问对象 (DAO)
2015/02/22 PHP
从性能方面考虑PHP下载远程文件的3种方法
2015/12/29 PHP
新手入门常用代码集锦
2007/01/11 Javascript
event.keyCode键码值表 附只能输入特定的字符串代码
2009/05/15 Javascript
jquery 单击li防止重复加载的实现代码
2010/12/24 Javascript
javascript采用数组实现tab菜单切换效果
2012/12/12 Javascript
jQuery操作checkbox选择(list/table)
2013/04/07 Javascript
如何使用json在前后台进行数据传输实例介绍
2013/04/11 Javascript
在js文件中如何获取basePath处理js路径问题
2013/07/10 Javascript
jQuery 处理页面的事件详解
2015/01/20 Javascript
深入理解JavaScript系列(34):设计模式之命令模式详解
2015/03/03 Javascript
JavaScript定时器实现的原理分析
2016/12/06 Javascript
js实现打地鼠小游戏
2017/02/13 Javascript
Vue项目webpack打包部署到服务器的实例详解
2017/07/17 Javascript
详解如何在Node.js的httpServer中接收前端发送的arraybuffer数据
2018/11/11 Javascript
Vue项目引发的「过滤器」使用教程
2019/03/12 Javascript
[01:15:56]2018DOTA2亚洲邀请赛3月30日 小组赛A组 TNC VS Newbee
2018/03/31 DOTA
python函数参数*args**kwargs用法实例
2013/12/04 Python
使用Python对IP进行转换的一些操作技巧小结
2015/11/09 Python
python3第三方爬虫库BeautifulSoup4安装教程
2018/06/19 Python
Python Matplotlib 基于networkx画关系网络图
2019/07/10 Python
python中的列表与元组的使用
2019/08/08 Python
python 画条形图(柱状图)实例
2020/04/24 Python
Python selenium模块实现定位过程解析
2020/07/09 Python
python多线程和多进程关系详解
2020/12/14 Python
移动端Web页面的CSS3 flex布局快速上手指南
2016/05/31 HTML / CSS
C语言中一个结构不能包含指向自己的指针吗
2012/05/25 面试题
大学生军训广播稿
2014/01/24 职场文书
书法比赛获奖感言
2014/02/10 职场文书
竞聘书模板
2014/03/31 职场文书
社区科普工作方案
2014/06/03 职场文书
2015年小学数学教师个人工作总结
2015/05/25 职场文书
幼儿园庆六一主持词
2015/06/30 职场文书
pytorch 使用半精度模型部署的操作
2021/05/24 Python
Python中Selenium对Cookie的操作方法
2021/07/09 Python