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分页显示制作详细讲解
Oct 09 PHP
改进的IP计数器
Oct 09 PHP
PHP伪静态写法附代码
Jun 20 PHP
php中计算程序运行时间的类代码
Nov 03 PHP
php生成N个不重复的随机数实例
Nov 12 PHP
实现获取http内容的php函数分享
Feb 16 PHP
yii操作session实例简介
Jul 31 PHP
PHP移动文件指针ftell()、fseek()、rewind()函数总结
Nov 18 PHP
PHP简单判断iPhone、iPad、Android及PC设备的方法
Oct 11 PHP
php将print_r处理后的数据还原为原始数组的解决方法
Nov 02 PHP
php 文件下载 出现下载文件内容乱码损坏的解决方法(推荐)
Nov 16 PHP
PHP面向对象程序设计之类与反射API详解
Dec 02 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页面缓存方法小结
2015/01/10 PHP
ThinkPHP框架里隐藏index.php
2016/04/12 PHP
[原创]php简单防盗链验证实现方法
2016/07/09 PHP
php调用云片网接口发送短信的实现方法
2017/10/25 PHP
用JavaScript显示随机图像或引用
2009/04/21 Javascript
defer属性导致引用JQuery的页面报“浏览器无法打开网站xxx,操作被中止”错误的解决方法
2010/04/27 Javascript
基于JQuery实现的类似购物商城的购物车
2011/12/06 Javascript
jQuery判断div随滚动条滚动到一定位置后停止
2014/04/02 Javascript
jQuery trigger()方法用法介绍
2015/01/13 Javascript
js中跨域方法原理详解
2015/07/19 Javascript
JS锚点的设置与使用方法
2016/09/05 Javascript
微信小程序  简单实例(阅读器)的实例开发
2016/09/29 Javascript
微信小程序 动态的设置图片的高度和宽度详解及实例代码
2017/02/24 Javascript
详解如何使用nvm管理Node.js多版本
2019/05/06 Javascript
React精髓!一篇全概括小结(急速)
2019/05/23 Javascript
nodejs实现获取本地文件夹下图片信息功能示例
2019/06/22 NodeJs
微信小程序把百度地图坐标转换成腾讯地图坐标过程详解
2019/07/10 Javascript
JavaScript HTML DOM 元素 (节点)新增,编辑,删除操作实例分析
2020/03/02 Javascript
vuex的使用步骤
2021/01/06 Vue.js
Python中的高级函数map/reduce使用实例
2015/04/13 Python
浅谈Python 对象内存占用
2016/07/15 Python
利用Python画ROC曲线和AUC值计算
2016/09/19 Python
Python中列表list以及list与数组array的相互转换实现方法
2017/09/22 Python
python针对excel的操作技巧
2018/03/13 Python
python实现多进程代码示例
2018/10/31 Python
使用pyshp包进行shapefile文件修改的例子
2019/12/06 Python
python统计文章中单词出现次数实例
2020/02/27 Python
解决Keras 自定义层时遇到版本的问题
2020/06/16 Python
python开发入门——列表生成式
2020/09/03 Python
THE OUTNET英国官网:国际设计师品牌折扣网站
2016/08/14 全球购物
战友聚会邀请函
2014/01/18 职场文书
人力资源经理的岗位职责范本
2014/02/28 职场文书
信用卡逾期证明示例
2014/09/13 职场文书
个人债务授权委托书范本
2014/10/05 职场文书
大学优秀学生主要事迹材料
2015/11/04 职场文书
Nginx下配置Https证书详细过程
2021/04/01 Servers