一致性hash

场景

       随着数据量的增大,现在很多单机服务器压力也在逐渐增大,到某一阶段撑不住了,一般情况下都会考虑集群,或者分片处理。分片的话就会考虑到一个问题怎么确定某一个key值对应的服务器或者片的位置。一般情况下都是几种方式来处理,之后会讲到几种hash算法的优缺点,在这之前首先了解一下一致性的几个原则。

均衡性(Balance)

平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。

单调性(Monotonicity)

单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。(这段翻译信息有负面价值的,当缓冲区大小变化时一致性哈希(Consistent hashing)尽量保护已分配的内容不会被重新映射到新缓冲区。)


一、求余

        求余算是我们一般情况下比较常见的hash算法,例如假设我们有5台服务器,然后我们把对应的key与5求余,得到的数就是我们对应服务器的下标。这种方式比较常见,比如数据库分表等有些会才用这种方式去处理。但是这种方式有一个明显的缺点,那就是不利于扩展,假设我们有一台服务器挂掉了,或者我们要新增加一台服务器,这个时候,求余的基数就发生了改变,那么很大一部分数据都会受到影响。这明显不符合单调性,当然也有解决办法,那就是每次增加服务器都是成倍增加,然后新的数据可以对应映射到新的服务器上,老的数据不变。当然这样成本比较高。

二、环形hash空间(hash环)

考虑通常的 hash 算法都是将 value 映射到一个 32 为的 key 值,也即是 0~2^32-1 次方的数值空间;我们可以将这个空间想象成一个首( 0 )尾( 2^32-1 )相接的圆环,如下面图 1 所示的那样。

然后我们把服务器节点映射到对应hash数值空间中,一般情况可以根据服务器ip地址+端口号的方式hash。然后我们把key按照同样的hash算法一样映射到hash环上,接下来要考虑的就是如何将对象映射到 cache 服务器上去了。在这个环形空间中,如果沿着顺时针方向从对象的 key 值出发,直到遇见一个 cache ,那么就将该对象存储在这个 cache 上,因为对象和 cache 的 hash 值是固定的,因此这个 cache 必然是唯一和确定的。

前面讲过,通过 hash 然后求余的方法带来的最大问题就在于不能满足单调性,当 cache 有所变动时, cache 会失效,进而对后台服务器造成巨大的冲击,现在就来分析分析 consistent hashing 算法。

 移除 cache

考虑假设 cache B 挂掉了,根据上面讲到的映射方法,这时受影响的将仅是那些沿 cache B 逆时针遍历直到下一个 cache ( cache C )之间的对象,也即是本来映射到 cache B 上的那些对象。

添加 cache

再考虑添加一台新的 cache D 的情况,假设在这个环形 hash 空间中, cache D 被映射在对象 object2 和 object3 之间。这时受影响的将仅是那些沿 cache D 逆时针遍历直到下一个 cache ( cache B )之间的对象(它们是也本来映射到 cache C 上对象的一部分),将这些对象重新映射到 cache D 上即可。

如此一来,如果我们对服务器进行增减,只会影响到少部分服务器,并不会对导致大量的缓存都命中到其他服务器上去。但是缺点的话就是,如果我们服务器数量不多,有可能恰好服务器hash出来对应环上的位置某一个跟其他节点离的较远,而其他的相对间隔较近,这就会导致服务器上缓存分布不均匀。

三、虚拟节点

    为了解决实体节点数据分布不均匀的问题,我们可以采用增加虚拟节点的方式来应对,例如我们有10台实体服务器,我们可以为每台服务器设置1000个虚拟服务器,然后将虚拟服务器也按照对应的hash算法映射到hash环中,然后缓存key找到对应的虚拟服务器节点,再由虚拟服务器与实习服务器映射关系找到对应实体服务器。

<?php

// 一致性哈希算法

class ConsistentHashing

{

    protected $nodes = array();    //真实节点

    protected $position = array();  //虚拟节点

    protected $mul = 64;  // 每个节点对应64个虚拟节点

    /**

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

    */

    public function hash($str)

    {

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

    }

    /**

    * 核心功能

    */

    public function lookup($key)

    {

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

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

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

        // 循环获取相近的节点

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

            if ($point <= $key) {

                $node = $val;

                break;

            }

        }

        reset($this->position);    //把数组的内部指针指向第一个元素,便于下次查询从头查找

        return $node;

    }

    /**

    * 添加节点

    */

    public function addNode($node)

    {

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

        // 添加节点和虚拟节点

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

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

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

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

        }

        // 重新排序

        $this->sortPos();

    }

    /**

    * 删除节点

    */

    public function delNode($node)

    {

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

        // 循环删除虚拟节点

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

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

        }

        // 删除节点

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

    }

    /**

    * 排序

    */

    public function sortPos()

    {

        ksort($this->position, SORT_REGULAR);

    }

}

// 测试

$con = new ConsistentHashing();

$con->addNode('a');

$con->addNode('b');

$con->addNode('c');

$con->addNode('d');

$key1 = 'www.zhihu.com';

$key2 = 'www.baidu.com';

$key3 = 'www.google.com';

$key4 = 'www.testabc.com';

echo 'key' . $key1 . '落在' . $con->lookup($key1) . '号节点上!<br>';

echo 'key' . $key2 . '落在' . $con->lookup($key2) . '号节点上!<br>';

echo 'key' . $key3 . '落在' . $con->lookup($key3) . '号节点上!<br>';

echo 'key' . $key4 . '落在' . $con->lookup($key4) . '号节点上!<br>';

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容