网上已有多篇很详细的概念,这里不做赘述,简要说下自己关于一致性哈希算法的想法:
1.平凡hash算法
首先是hash算法,这里之前在学校习惯的取模算法并不可取。原因在于当整个hash空间的节点增加或者删除时,会导致所有节点的hash重新计算,导致了不单调。我在代码中选取了DJBX33A算法,该算法是对字符串而言目前已知的最好哈希算法。
2.hash空间
这里举一个简单的hash空间为例说明:该hash空间是个环形hash,避免单调性的问题,其次可以保证每一个输入进来的object在有target的存在下都会找到最靠近其的target.
3.映射
在该算法中需要操作的一个是object,一个是node,object是需要输入保存的信息,而node是缓存计算机节点。均通过hash函数来计算其hash值,并存于hash空间中。
4.节点的增删
毫无疑问,在hash算法中会面临缓存计算机down掉或者新加入一台缓存计算机,所以节点的增删对hash空间的影响应尽可能的小。
图中,node4节点被移除,则原本存于其的obj3进而找到距离其顺指针方向最近的node1,从而存在了node1中。
而在该图中,新加入了一个node5节点,则原本归属于node1的obj4归并到node5中。
5.虚拟节点
虚拟节点的作用在于,当down掉过多的相邻计算机,而增加了某个或某几个节点的负担时,可以通过虚拟节点来平衡其负载。原理是,将每一台服务器计算机当做一个虚拟计算机群。将原来的一个节点例如node1,分为node1.1, node1.2, node1.3并加入到原hash空间中。使得每一台计算机负载尽可能平衡。
6.冗余
在参考别人实现的时候发现冗余是将其归属的node之后几个节点均储存其object信息。觉得合理。
<?php
/**
* Created by PhpStorm.
* User: Jason D
* Date: 2016/4/15
* Time: 13:39
*/
functionssHash($str) {
$hash =
0;
$s =md5($str);
$seed =
5;
$len =
32;
for ($i
= 0;
$i <
$len;
$i++) {
$hash = ($hash
<< $seed) +
$hash + ord($s{$i});
}
return $hash
& 0x7FFFFFFF;
}
class Flexihash
{
private $_replicas
= 1;
private $_targetCount
= 0;
private $_positionToTarget
= array();
private $_targetToPositions
= array();
public $_sourceToPositions
= array();
private $_targetsource
= array();
private $_isSorted
= false;
public function
setReplicas($replicas)
{
$this->_replicas
= $replicas;
}
public function
addsource($source)
{
$hash = ssHash($source);
if(!isset($this->_sourceToPositions[$hash]))
$this->_sourceToPositions[$hash]
= $source;
return
$this;
}
public function
removeSource($source)
{
$hash = ssHash($source);
if(isset($this->_sourceToPositions[$hash]))
unset($this->_sourceToPositions[$hash]);
$target =
$this->find($source);
for($i
= 0;
$i < cout($this->_targetsource);
$i++)
{
if($this->_targetsource[$i]
== $source)
unset($this->_targetsource[$i]);
}
}
public function
addTarget($target)
{
if(isset($this->_targetToPositions[$target]))
{
echo"this Target alreadyexists.";
}
$this->_targetToPositions[$target]
= array();
for($i
= 0;
$i <
$this->_replicas;
$i++)
{
$position
= ssHash($target
. $i);
$this->_positionToTarget[$position]
= $target;
$this->_targetToPositions[$target][]
= $position;
}
$this->_isSorted
= false;
$this->_targetCount
++;
return
$this;
}
public function
addTargets($targets)
{
foreach($targets
as $target)
{
addTarget($target);
}
return
$this;
}
public function
removeTarget($target)
{
if(!isset($this->_targetToPositions[$target]))
{
echo
"this Target does notexist.";
}
foreach($this->_targetToPositions[$target]
as $position)
{
unset($this->_positionToTarget[$position]);
}
unset($this->_targetToPositions[$target]);
$this->_targetCount--;
return
$this;
}
public function
getAllTargets()
{
return
array_keys($this->_targetToPositions);
}
public function
lookup($resource)
{
$targets =
$this->lookupList($resource,
1);
if(empty($targets))
echo
"No targets exist";
return
$targets[0];
}
public function
find()
{
foreach($this->_sourceToPositions
as $key
=> $value)
{
$target
= lookup($value);
if(empty($target))
echo
"no targetexist";
$this->_targetsource[$target]
= array();
$_targetsource[$target][] =
$value;
}
return
$target;
}
public function
lookupList($resource,
$requestedCount)
{
if(!$requestedCount)
echo
"Invalid count requested";
if(empty($this->_positionToTarget))
return array();
if($this->_targetCount
== 1)
return
array_unique(array_values($this->_positionToTarget));
$resourcePosition
= ssHash($resource);
$results =
array();
$collect =
false;
$this->_sortPositionTargets();
foreach($this->_positionToTarget
as $key
=> $value)
{
if(!$collect
&& $key
> $resourcePosition)
{
$collect
= true;
}
if($collect
&& !in_array($value,
$results))
{
$results
[] = $value;
}
if(count($results) ==
$requestedCount || count($results) ==
$this->_targetCount)
{
return
$results;
}
}
return
$results;
}
private function
_sortPositionTargets()
{
if(!$this->_isSorted)
{
ksort($this->_positionToTarget,
SORT_REGULAR);
$this->_isSorted
= true;
}
}
}