網上已有多篇很詳細的概念,這裏不做贅述,簡要說下自己關於一致性哈希算法的想法:
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;
}
}
}