memcache一致性hash的php实现方法
本文实例讲述了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
二分法在虚拟节点环中查找最近的一个虚拟节点
从虚拟节点中提取真实的memcacheip和端口,做单例连接
<?php
classmemcacheHashMap{
private$_node=array();
private$_nodeData=array();
private$_keyNode=0;
private$_memcache=null;
//每个物理服务器生成虚拟节点个数[注:节点数越多,cache分布的均匀性越好,同时setget操作时,也更耗资源,10台物理服务器,采用200较为合理]
private$_virtualNodeNum=200;
privatefunction__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)thrownewException('CacheconfigNULL');
foreach($configas$key=>$value){
for($i=0;$i<$this->_virtualNodeNum;$i++){
$this->_node[sprintf("%u",crc32($value.'_'.$i))]=$value.'_'.$i;//循环为每个memcache服务器创建200个虚拟节点
}
}
ksort($this->_node);//创建出来的1000个虚拟节点按照键名从小到大排序
}
//实例化该类
staticpublicfunctiongetInstance(){
static$memcacheObj=null;
if(!is_object($memcacheObj)){
$memcacheObj=newself();
}
return$memcacheObj;
}
//根据传来的键查找到对应虚拟节点的位置
privatefunction_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)thrownewException('CacheconfigError');
if(!isset($this->_memcache[$config])){
$this->_memcache[$config]=newMemcache;
list($host,$port)=explode(':',$config);
$this->_memcache[$config]->connect($host,$port);
}
return$this->_memcache[$config];
}
//二分法根据给出的值找出最近的虚拟节点位置
privatefunction_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);
elsereturn$this->_findServerNode($avg+1,$b);
}
if(abs($this->_nodeData[$b]-$this->_keyNode)<abs($this->_nodeData[$m]-$this->_keyNode)) return$this->_nodeData[$b];
elsereturn$this->_nodeData[$m];
}
publicfunctionset($key,$value,$expire=0){
return$this->_connectMemcache($key)->set($key,json_encode($value),0,$expire);
}
publicfunctionadd($key,$value,$expire=0){
return$this->_connectMemcache($key)->add($key,json_encode($value),0,$expire);
}
publicfunctionget($key){
returnjson_decode($this->_connectMemcache($key)->get($key),true);
}
publicfunctiondelete($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=newMemcache;
$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程序设计有所帮助。