一致性hash算法 Php,php实现一致性hash算法

使用场景

比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;求余算法: hash(object)%N

一切都运行正常,再考虑如下的两种情况;

1 一个 cache 服务器 m down 掉了(在实际应用中必须要考虑这种情况),这样所有映射到 cache m 的对象都会失效,怎么办,需要把 cache m 从 cache 中移除,这时候 cache 是 N-1 台,映射公式变成了 hash(object)%(N-1) ;

2 由于访问加重,需要添加 cache ,这时候 cache 是 N+1 台,映射公式变成了 hash(object)%(N+1) ;

1 和 2 意味着什么?这意味着突然之间几乎所有的 cache 都失效了。对于服务器而言,这是一场灾难,洪水般的访问都会直接冲向后台服务器;

Consistent Hashing 一致性hash的原理

consistent hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在key 映射关系,尽可能的满足单调性的要求。

code代码实现<?php

class ConsistentHash

{

protected $nodes;

protected $replicaNum;

private $positions = [];

/**

* 把字符串转为32位符号整数

* @param $str

* @return string

*/

public function hash($str)

{

return sprintf('%u', crc32($str));

}

/**

* ConsistentHash constructor.

* @param int $replicaNum 数值越大散列越好,推荐最小为16,数值为2的指数幂

*/

public function __construct($replicaNum)

{

$this->replicaNum = $replicaNum;

}

/**

* 定位key所在节点

* @param $key

* @return mixed

*/

public function locateKey($key)

{

$point = $this->hash($key);

// 先取圆环上最小的一个节点,当成结果

$node = current($this->positions);

// 循环获取相近的节点

foreach ($this->positions as $key => $val) {

if ($point <= $key) {

$node = $val;

break;

}

}

reset($this->positions);

return $node;

}

public function addNode($node)

{

if (isset($this->nodes[$node])) return;

// 添加节点和虚拟节点

for ($i = 0; $i < $this->replicaNum; $i++) {

$pos = $this->hash($node . '#' . $i);

$this->positions[$pos] = $node;

$this->nodes[$node][] = $pos;

}

// 重新排序

$this->sortPos();

}

public function removeNode($node)

{

if (!isset($this->nodes[$node])) return;

// 循环删除虚拟节点

foreach ($this->nodes[$node] as $val) {

unset($this->positions[$val]);

}

// 删除节点

unset($this->nodes[$node]);

}

public function sortPos()

{

ksort($this->positions, SORT_REGULAR);

}

}

改变replicaNum测试散列效果(测试代码)$hash = new ConsistentHash(256);

$hash->addNode('192.168.0.1');

$hash->addNode('192.168.0.2');

$hash->addNode('192.168.0.3');

$hash->addNode('192.168.0.4');

$hash->addNode('192.168.0.5');

$data = [];

$limit = 10000;

for ($i = 0; $i <= $limit; $i++) {

$key = uniqid(uniqid());

$data[$hash->locateKey($key)][] = $key;

}

$average = bcdiv($limit, count($data));

$variance = 0;

foreach ($data as $node => $keys) {

$num = count($keys);

$variance += pow(abs($num - $average), 2);

echo $node . ' - ' . $num . PHP_EOL;

}

echo '方差值为:' . $variance . PHP_EOL;

测试结果8

192.168.0.2 - 5374

192.168.0.3 - 4000

192.168.0.1 - 126

192.168.0.4 - 419

192.168.0.5 - 82

方差值为:25074037

16

192.168.0.3 - 2578

192.168.0.2 - 1569

192.168.0.1 - 2530

192.168.0.4 - 1539

192.168.0.5 - 1785

方差值为:1059491

32

192.168.0.3 - 1733

192.168.0.4 - 2112

192.168.0.5 - 1765

192.168.0.2 - 1632

192.168.0.1 - 2759

方差值为:850563

64

192.168.0.2 - 1411

192.168.0.3 - 2502

192.168.0.5 - 2141

192.168.0.4 - 1868

192.168.0.1 - 2079

方差值为:642471

128

192.168.0.1 - 2108

192.168.0.4 - 2032

192.168.0.3 - 2073

192.168.0.2 - 1417

192.168.0.5 - 2371

方差值为:495547

256

192.168.0.4 - 1922

192.168.0.1 - 2501

192.168.0.2 - 1969

192.168.0.5 - 1908

192.168.0.3 - 1701

方差值为:355911

结语

通过测试结果可知,推荐replicaNum最小值为16,再考虑性能的情况下,最大值设为64

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值