一致性hash详解

需求

  我们都知道,任何一种算法的的出现都源于需求。人们需要用它来更好的解决问题。那么,一致性hash算法的出现是源于什么需求呢?例如:在使用n台缓存服务器时,一种常用的负载均衡方式是,对资源o的请求使用hash(o)=o mod n 来映射到某一台缓存服务器。当增加一台服务器时,其hash函数相应就得变为:hash(o)=o mod (n + 1);反之,当 减少一台缓存服务器时,其hash函数相应就得变为:hash(o)=o mod (n - 1);这两种方式会改变所有资源对应的hash值,也就是所有的缓存都失效了,这会使得缓存服务器大量集中地向原始内容服务器更新缓存。
  因此需要一致哈希算法来避免这样的问题。

一致性Hash算法背景

  一致哈希由MIT的Karger及其合作者提出,现在这一思想已经扩展到其它领域。在这篇1997年发表的学术论文中介绍了“一致哈希”如何应用于用户易变的分布式Web服务中。哈希表中的每一个代表分布式系统中一个节点,在系统添加或删除节点只需要移动 K/n项。
  一致哈希也可用于实现健壮缓存来减少大型Web应用中系统部分失效带来的负面影响。
  一致哈希的概念还被应用于分布式散列表(DHT)的设计。DHT使用一致哈希来划分分布式系统的节点。所有关键字都可以通过一个连接所有节点的覆盖网络高效地定位到某个节点。

特性

  一致性hash算法提出了在动态变化的Cache环境中,应该满足一下几个方面:平衡性,单调性,分散性,负载,平滑性。

平衡性(Balance)

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

单调性(Monotonicity)

  单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。简单的哈希算法往往不能满足单调性的要求,如最简单的线性哈希:x = (ax + b) mod (P),在上式中,P表示全部缓冲的大小。不难看出,当缓冲大小发生变化时(从P1到P2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。哈希结果的变化意味着当缓冲空间发生变化时,所有的映射关系需要在系统内全部更新。而在P2P系统内,缓冲的变化等价于Peer加入或退出系统,这一情况在P2P系统中会频繁发生,因此会带来极大计算和传输负荷。单调性就是要求哈希算法能够应对这种情况。

分散性(Spread)

  在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。

负载(Load)

  负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

平滑性(Smoothness)

  平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致的。

设计思路

  一致哈希 是一种特殊的哈希算法。在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n 个关键字重新映射,其中 K是关键字的数量。n是槽位数量。然而在传统的哈希表中,添加或删除一个槽位的几乎需要对所有关键字进行重新映射。

环形hash空间

   一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个哈希空间环如下:
   这里写图片描述 

数据

  把需要缓存的内容(对象)映射到hash 空间。通过 hash 函数计算出的 hash 值 key 。

hash(object1) = key1;
hash(object2) = key2;
hash(object3) = key3;
hash(object4) = key4;

  如下图:

这里写图片描述 

服务器节点

  一致性hash 的基本思想就是将对象和 cache 都映射到同一个 hash 数值空间中,并且使用相同的 hash算法。将机器通过hash算法映射到环上(一般情况下对机器的hash计算是采用机器的IP或者机器唯一的别名作为输入值)然后以顺时针的方向计算,将所有对象存储到离自己最近的机器中。
  假设当前有 1,2,3 共 3 台服务器(节点),那么其映射结果如下图:

hash(cache A) = key A;
hash(cache B) = key B;
hash(cache C) = key C;

这里写图片描述

然后以顺时针的方向计算,将所有对象存储到离自己最近的机器中。

服务器节点的管理

  普通hash求余算法最为不妥的地方就是在有机器的添加或者删除之后会照成大量的对象存储位置失效,这样就大大的不满足单调性了。下面我们就来介绍一下一致性hash的处理方式:

添加

  当添加一台新的机器 cache D 的情况,假设在这个环形 hash 空间中, cache D 被映射在对象 object2 和object3 之间。这时受影响的将仅是那些沿 cache D 逆时针遍历直到下一个 cache 之间的对象,按顺时针迁移的原则,将这些对象重新映射到 cache D 上即可。
  因此这里仅需要变动对象 object2 ,将其重新映射到 cache D 上;
  这里写图片描述 

删除

  若 cache B 挂掉了,这时受影响的将仅是那些沿 cache B 逆时针遍历直到下一个 cache 之间的对象。
  按照顺时针迁移的原则,因此这里仅需要变动对象 object4 ,将其重新映射到 cache C 上即可;
  这里写图片描述 

通过按顺时针迁移的规则,当节点发生改变的时候,仅影响两个节点之间的对象,其它对象还保持这原有的存储位置。通过对节点的添加和删除的分析,一致性哈希算法在保持了单调性的同时,还是数据的迁移达到了最小,这样的算法对分布式集群来说是非常合适的,避免了大量数据迁移,减小了服务器的的压力。

虚拟节点

  在上述操作中,当我们删除节点之后。很明显的cache A 仅存储了 object1 ,而 cache C则存储了object2 、 object3 和 object4 ;分布是很不均衡的。违背了平衡性原则(平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去)。
  这里写图片描述
  所以,为了解决这种情况, 引入了“虚拟节点”的概念。下面我们先了解一下什么是虚拟节点?
  答::“虚拟节点”( virtual node )是实际节点在 hash 空间的复制品( replica ),一实际个节点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以 hash 值排列。
  以上图为例,现在我们引入虚拟节点,并设置“复制个数”为 2 , cache A1, cache A2 代表了 cache A; cache C1, cache C2 代表了 cache C ;如下图:
  这里写图片描述
  映射到虚拟节点之后,我们再将相应的对象映射到其真实节点。因此对象 object1 和 object2 都被映射到了 cache A 上,而 object3 和 object4 映射到了 cache C 上;平衡性有了很大提高。
  引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询物体所在 cache 时的映射关系如图 :
  这里写图片描述
  

参考资料:
  简书:https://www.jianshu.com/p/e8fb89bb3a61
  维基百科:
  https://zh.wikipedia.org/wiki/%E4%B8%80%E8%87%B4%E5%93%88%E5%B8%8C
  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值