php 字典排序哈希,PHP 的哈希算法

PHP 内核实现的哈希算法

static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)

{

register ulong hash = 5381;

/* variant with the hash unrolled eight times */

for (; nKeyLength >= 8; nKeyLength -= 8) {

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

hash = ((hash << 5) + hash) + *arKey++;

}

switch (nKeyLength) {

case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */

case 1: hash = ((hash << 5) + hash) + *arKey++; break;

case 0: break;

EMPTY_SWITCH_DEFAULT_CASE()

}

return hash;

}

参照内核实现的哈希算法

function hash0($str)

{

$length = strlen($str);

// 不过测试中填 0 显得分散性更好

$hash = 5381;

$i = 0;

for (; $length >= 8; $length -= 8) {

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

}

switch ($length) {

case 7:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 6:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 5:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 4:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 3:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 2:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 1:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

break;

case 0:

break;

}

// 表示 hash 最大值为 0x7FFFFFFF

return $hash & 0x7FFFFFFF;

}

// 测试

$str = 'helloworldddfdfdfdfdsfdsdffdsfddfdsfdsfssaafd';

$hash = hash0($str);

echo $hash % 7; // 输出

一致性哈希

一致性哈希算法出现的原因

在分布式缓存中,如果直接通过对缓存的键哈希然后与服务器的个数取模的方式,当加入新服务器时,会造成整个分布式缓存系统中的缓存失效,因为需要重新对服务器数取模。

一致性哈希算法的原理

假设有 k 个服务器,数据的哈希值范围为 [0,MAX],我们可以把哈希值分成 m 个区,每个机器负责 m / k 个区的缓存,当新来一台服务器时,我们只需把某一个区的缓存从原来服务器移动到新服务器就可以了。

上面说的是一致性哈希的基本思想。除此之外,还会借助一个虚拟的环和虚拟节点。

虚拟的环就是把缓存键和服务器的哈希值,放到一个 [0,2^32] 的圆上,每台服务器负责存储与上一台服务器顺时针方向的之间的值,当新加入一台服务器时,只需要将这台服务器与上一台服务器之间的缓存移动到该服务器上,就可以了。

虚拟节点,就是一台服务器往往哈希多个值,以减轻数据倾斜问题,一般是一台服务器有 32 个虚拟节点。

一致性哈希算法

class ConsistentHash

{

protected $servers = [];

protected $virtualServers;

protected $virtualServerNum;

/**

* @param array $servers 服务器

* @param int $virtualServerNum 虚拟节点个数

*/

public function __construct(array $servers, $virtualServerNum = 32)

{

$this->virtualServerNum = $virtualServerNum;

$this->addServers($servers);

}

public function getServers()

{

return $this->servers;

}

/**

* 添加服务器

*

* @param $servers

*/

public function addServers($servers)

{

if (! is_array($servers)) {

$servers = [$servers];

}

$this->servers = array_merge($this->servers, $servers);

foreach ($servers as $server) {

$i = 1;

while ($i <= 32) {

$virtualNode = $server.'#'.$i;

$this->virtualServers[$virtualNode] = $this->hash($virtualNode);

$i++;

}

}

asort($this->virtualServers);

}

/**

* 删除服务器

*

* @param $server

* @return bool

*/

public function deleteServer($server)

{

$i = 1;

$delKey = array_search($server, $this->servers);

if ($delKey !== false) {

unset($this->servers[$delKey]);

}

while ($i <= 32) {

$virtualNode = $server.'#'.$i;

unset($this->virtualServers[$virtualNode]);

$i++;

}

return true;

}

/**

* 添加数据

*

* @param $key

* @param $data

* @return string|string[]|null

*/

public function add($key, $data)

{

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

foreach ($this->virtualServers as $server => $val) {

if ($hash <= $val) {

return $this->subServer($server);

}

}

reset($this->virtualServers);

return $this->subServer(key($this->virtualServers));

}

/**

* 获取数据

*

* @param $key

* @return string|string[]|null

*/

public function get($key)

{

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

foreach ($this->virtualServers as $server => $val) {

if ($hash <= $val) {

return $this->subServer($server);

}

}

reset($this->virtualServers);

return $this->subServer(key($this->virtualServers));

}

protected function subServer($server)

{

$pattern = '/#[0-9]+/';

return preg_replace($pattern, '', $server);

}

protected function hash($str)

{

$length = strlen($str);

$hash = 5381;

$i = 0;

for (; $length >= 8; $length -= 8) {

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

}

switch ($length) {

case 7:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 6:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 5:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 4:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 3:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 2:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

case 1:

$hash = (($hash << 5) + $hash) + ord($str[$i++]);

break;

case 0:

break;

}

return $hash & 0x7FFFFFFF;

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值