网上已有多篇很详细的概念,这里不做赘述,简要说下自己关于一致性哈希算法的想法:
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;
}
}
}