17一致性哈希算法

       问题描述:假如QQ有n个服务器,为了方便用户的访问会在服务器上缓存据,因此用户每次访问的时候最好能保持同一台服务器。现有的做法是根据[QQNUM%n]得到请求的服务器,这种方法很方便将用户分到不同的服务器上去。

       但是如果一台服务器死掉了,那么n就变为了n-1,那么[QQNUM%n]与[QQNUM%(n-1)]基本上都不一样了,所以大多数用户的请求都会转到其他服务器,这样会发生大量访问错误。

 

       问:如何改进或者换一种方法,使得:
1)一台服务器死掉后,不会造成大面积的访问错误,
2)原有的访问基本还是停留在同一台服务器上;
3)尽量考虑负载均衡。

      

       最土的办法还是用模余方法:做法很简单,假设有N台服务器,现在完好的是MM<=N),先用N求模[QQNUM%N],如果落在损坏的机器上,然后再用N-1求模,直到M。这种方式对于坏的机器不多的情况下,具有更好的稳定性。

 

       现在介绍一种更先进的算法:一致性哈希算法

 

       在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括:  轮循算法(Round Robin)、哈希算法(HASH)、最少连接算法(Least Connection)、响应速度算法(Response Time)、加权法(Weighted)等。其中哈希算法是最为常用的算法.

       典型的应用场景是:有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均分发到每台服务器上,每台机器负责1/N的服务。

       常用的算法是对hash结果取余数 (hash() mod N):对机器编号从0到N-1,按照自定义的hash()算法,对每个请求的hash()值按N取模,得到余数i,然后将请求分发到编号为i的机器。

 

       但这样的算法方法存在致命问题,如果某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时需要将当掉的服务器从算法从去除,此时候会有(N- 1)/N的服务器的缓存数据需要重新进行计算;如果新增一台机器,会有N /(N+1)的服务器的缓存数据需要进行重新计算。对于系统而言,这意味着大量缓存的失效或者数据需要转移,这是不可接受的。

       那么,如何设计一个负载均衡策略,使得受到影响的请求尽可能的少呢?
       采用ConsistentHashing算法,可以说ConsistentHashing 是分布式系统负载均衡的首选算法。

 

一:Consistent Hashing算法描述

       Consistent hashing 算法早在 1997 年就在论文 Consistent hashing and randomtrees 中被提出,目前在 cache服务器 系统中应用越来越广泛。

 

       Hash 算法的一个衡量指标是单调性(Monotonicity,定义如下:

  单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。其实就是:在新增或者删减节点时, 同一个key访问到的值总是一样的。

容易看到,简单 hash 算法 hash(object)%N 难以满足单调性要求。

 

二:Consistent hashing 算法的原理

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

 

    下面就来按照 5 个步骤简单讲讲 consistent hashing 算法的基本原理。

1:环形hash 空间

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

 

2:把对象(比如QQ号)映射到hash 空间

       接下来考虑 4 个对象 object1 ~ object4 ,则:

hash(object1) =key1;

… …

hash(object4) =key4;

       通过 hash 函数计算出的 hash 值 key 在环上的分布如下图所示。

 

3:cache服务器映射到hash 空间

   Consistent hashing 的基本思想就是将对象和 cache服务器都映射到同一个 hash 数值空间中,并且使用相同的hash 算法。

    假设当前有 A,B 和 C 共 3 台 cache服务器 ,那么其映射结果将如下图 所示,他们在 hash 空间中,以对应的 hash值排列。

hash(cache服务器 A) = key A;

… …

hash(cache服务器 C) = key C;

       这里,cache服务器的 hash 计算,一般的方法可以使用 cache服务器的 IP 地址或者机器名作为hash 输入。

 

4:把对象映射到cache服务器

       现在 cache服务器和对象都已经通过同一个 hash 算法映射到 hash 数值空间中了,接下来要考虑的就是如何将对象映射到 cache服务器 上面了。

       在这个环形空间中,如果沿着顺时针方向从对象的 key 值出发,直到遇见一个 cache服务器 ,那么就将该对象存储在这个 cache服务器 上,因为对象 和 cache服务器 的 hash 值是固定的,因此这个 cache服务器 必然是唯一和确定的。

       参见上图,根据上面的方法,对象 object1 将被存储到 cache服务器 A 上; object2和 object3 对应到 cache服务器 C上 ; object4 对应到 cache服务器 B。

 

5:移除 cache服务器

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

       因此这里仅需要变动对象 object4 ,将其重新映射到 cache服务器 C 上即可,如下图:


 

6:添加 cache服务器

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

    因此这里仅需要变动对象 object2 ,将其重新映射到 cache服务器 D 上,如下图:

 

三:虚拟节点

       考量 Hash 算法的另一个指标是平衡性 (Balance) ,定义如下:

  平衡性是指哈希的结果能够尽可能平均的分布到所有的缓冲中去,这样可以使得所有的缓冲服务器都得到利用。

    上述Consistent hashing 算法并不是保证绝对的平衡,如果 cache服务器 较少的话,对象并不能被均匀的映射到 cache服务器 上,比如在上面的例子中,仅部署 cache服务器 A 和 cache服务器 C 的情况下,在 4 个对象中, cache服务器 A 仅存储了 object1 ,而 cache服务器 C 则存储了 object2 、 object3 和 object4 。分布是很不均衡的。

      

       为了解决这种情况, Consistent hashing 引入了“虚拟节点”的概念,它可以如下定义:

       虚拟节点 virtualnode )是实际节点在 hash 空间的复制品( replica ),一个实际节点对应了若干个虚拟节点,这个对应个数也成为复制个数虚拟节点 hash 空间中以 hash 值排列。

       仍以仅部署 cache服务器 A 和 cache服务器 C 的情况为例,在图 4 中我们已经看到, cache服务器 分布并不均匀。现在我们引入虚拟节点,并设置“复制个数”为 2 ,这就意味着一共会存在 4 个“虚拟节点”, cache服务器 A1和cache服务器A2 代表了 cache服务器A;cache服务器C1和cache服务器C2 代表了cache服务器 C。假设一种比较理想的情况,参见下图:

 

       此时,对象到“虚拟节点”的映射关系为:

       objec1->cache服务器 A2 ; objec2->cache服务器 A1 ; objec3->cache服务器 C1 ; objec4->cache服务器 C2 。

因此对象 object1 和 object2 都被映射到了cache服务器A 上,而 object3 和 object4 映射到了cache服务器 C 上;平衡性有了很大提高。

 

    引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询对象所在 cache服务器时的映射关系下图所示:

 

       “虚拟节点”的 hash 计算可以采用对应节点的 IP 地址加数字后缀的方式。

       例如假设 cache服务器 A 的 IP 地址为202.168.14.241 。引入“虚拟节点”前,计算 cache服务器 A 的 hash 值:Hash(“202.168.14.241”);引入“虚拟节点”后,计算“虚拟节”点 cache服务器 A1 和 cache服务器 A2 的 hash 值:

Hash(“202.168.14.241#1”);  //cache服务器 A1

Hash(“202.168.14.241#2”);  //cache服务器 A2

 

(http://blog.csdn.net/sparkliang/article/details/5279393)

 

原文地址:https://www.cnblogs.com/gqtcgq/p/7247170.html