一致性hash算法原理及php实现

在使用分布式存储时我们要考虑如何将数据均匀分布在多个节点库中?并且也要方便后期的数据查找。

hash取模

假设有N个存储节点,根据用户传入的key(key具有唯一性),index = hash(key) % N计算出将数据存储在哪一个节点上。

一致性hash

为什么不直接使用hash取模的方式,主要原因是:hash取模在容错性和扩展性上较差,如果新增一个节点,或者删除一个节点,那么所有的几点都要重新计算一遍。显然不符合实际实际生产环境。

什么是一致性hash?

通过一致性hash算法可以把key的hash值均匀分布在一个hash环上,hash环的范围是 0 ~ 2^32-1。通过节点的ip,port、hostname等信息hash后分布到hash环上。同时为了均匀分布,可为每个节点设置m个虚拟节点。
在这里插入图片描述

如上图所示:有节点N1,N2,N3。并且给这三个节点都设置2个虚拟节点。 数据的存储按照hash环顺时针,将hash运算后的key存储在下一个临近节点上。

如果需要删除或者新增一个节点,只会有一小部分key需要重新计算或者转移,一致性hash无论是扩展性还是容错性上都是要优于hash取模的。

php实现一致性hash算法
class ConsistentHash
{

    public $serverList = []; //服务器列列表
    public $virtualPos = []; //虚拟节点的位置
    public $virtualPosNum = 2;  //每个节点对应2个虚节点

    /**
     * 使用循环冗余算法计算出十进制校验值
     * @param $str
     * @return int
     */
    public function cHash($str)
    {
        $str = md5($str);
        return sprintf('%u', crc32($str));
    }

    /**
     * 在当前的服务器列表中找到合适的服务器存放数据
     * @param $key 键名
     * @return mixed 返回服务器IP地址
     */
    public function lookup($key)
    {
        $point = $this->cHash($key);//落点的hash值
        $finalServer = current($this->virtualPos);//先取圆环上最小的一个节点当成结果
        foreach ($this->virtualPos as $pos => $server) {
            if ($point <= $pos) {
                $finalServer = $server;
                break;
            }
        }
        reset($this->virtualPos);//重置圆环的指针为第一个
        return $finalServer;
    }

    /**
     * 添加一台服务器到服务器列表中
     * @param $server 服务器IP地址
     * @return bool
     */
    public function addServer($server)
    {
        if (!isset($this->serverList[$server])) {
            for ($i = 0; $i < $this->virtualPosNum; $i++) {
                $pos = $this->cHash($server . '-' . $i);
                $this->virtualPos[$pos] = $server;
                $this->serverList[$server][] = $pos;
            }
            ksort($this->virtualPos, SORT_NUMERIC);
        }
        return true;
    }

    /**
     * 移除一台服务器(循环所有的虚节点,删除值为该服务器地址的虚节点)
     * @param $key
     * @return bool
     */
    public function removeServer($key)
    {
        if (isset($this->serverList[$key])) {
            //删除对应虚节点
            foreach ($this->serverList[$key] as $pos) {
                unset($this->virtualPos[$pos]);
            }
            //删除对应服务器
            unset($this->serverList[$key]);
        }
        return true;
    }

}

#测试
array(4) {
  '192.168.1.1' =>
  array(2) {
    [0] =>
    string(9) "998838913"
    [1] =>
    string(10) "2388609030"
  }
  '192.168.1.2' =>
  array(2) {
    [0] =>
    string(9) "336885862"
    [1] =>
    string(10) "3749174810"
  }
  '192.168.1.3' =>
  array(2) {
    [0] =>
    string(10) "1624092728"
    [1] =>
    string(8) "65155740"
  }
  '192.168.1.4' =>
  array(2) {
    [0] =>
    string(10) "3733784813"
    [1] =>
    string(10) "1716255089"
  }
}
/home/www/test/ConsistentHash.php:97:
array(8) {
  [65155740] =>
  string(11) "192.168.1.3"
  [336885862] =>
  string(11) "192.168.1.2"
  [998838913] =>
  string(11) "192.168.1.1"
  [1624092728] =>
  string(11) "192.168.1.3"
  [1716255089] =>
  string(11) "192.168.1.4"
  [2388609030] =>
  string(11) "192.168.1.1"
  [3733784813] =>
  string(11) "192.168.1.4"
  [3749174810] =>
  string(11) "192.168.1.2"
}
/home/www/test/ConsistentHash.php:98:
string(11) "192.168.1.4"
/home/www/test/ConsistentHash.php:99:
string(11) "192.168.1.3"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值