时间:2014.07.17
地点:基地地板
----------------------------------------------------------------------------------------
一、为什么我们须要一致性哈希算法
考虑一个场景:server负载均衡问题。有n台server,比方n台cache。cache编号的选择和object的匹配应该採取什么用的策略才干满足保证功能的需求。
假设我们採取例如以下简单的哈希映射关系:
hash(object) mod n
看起来这样一个系统工作正常,但考虑周到会有例如以下几个问题:
问题1:有一天我们须要添加一台server。这时我们须要改变哈希映射关系为:
hash(object) mod (n+1)
问题2:有一天我们须要删除一台server,这时我们须要改变哈希映射关系为:
hash(object) mod (n-1)
问题3:怎么样使得server负载分配均匀
如今我们看到麻烦来了,因为hash关系的变化。差点儿全部的object将会被hash到新的位置,即映射到新的server,这是一种灾难,于是我们就须要一致性哈希来改善这样的情况了。
----------------------------------------------------------------------------------------
二、一致性哈希
一致性哈希能够保证当随意一台server添加到系统或从系统中删除时,只唯独相关的有限个object须要又一次匹配。即一致性哈希最大程度地防止object与server之前的匹配关系。----------------------------------------------------------------------------------------
三、哈希空间(hash space)
一般地,哈希函数将object映射到一个位的值上,哈希值取值范围为[0~2^32-1]。例如以下图所看到的。我们把哈希值域首尾相接联合成一个环形。于是也称呼为环形哈希空间。
哈希空间
----------------------------------------------------------------------------------------
四、将object映射到哈希空间
如果有4个object,分别为object1~object4,如今我们使用哈希函数获得他们各自的key值。并映射到环形哈希空间中去。例如以下图所看到的:hash(object1)=key1;
hash(object2)=key2;
hash(object3)=key3;
hash(object4)=key4;
将object映射到哈希空间
----------------------------------------------------------------------------------------
五、将cache映射到哈希空间
我们採用相同的哈希函数。继续还将server也映射到该环形哈希空间中,如果我们有3台serverA,B,C,哈希之后例如以下:
hash(cacheA)=keyA;
hash(cacheB)=keyB;
hash(cacheC)=keyC;
将cache以相同的方式也映射到哈希空间
----------------------------------------------------------------------------------------
六、将object与cache匹配
经过之前的步骤,如今object和cache都已经成功映射到环形哈希空间去了。接下来,我们将决定objects怎么和cache形成映射:我们採取的策略是,将object按顺时针方向走。直到找到第一个cache,若果该cache是可用的,即形成object和cache匹配,否则继续寻找下一个cache。依据上述原则,在这里我们得到的匹配结果是:
object1——>cacheA
object2——>cacheC
object3——>cacheC
object4——>cacheB
----------------------------------------------------------------------------------------
七、添加或删减cache
如今考虑两种场景:1.当某一cache崩溃移除系统 ,因为object4是映射在cacheB上的,如今cacheB将被移除了。那么如今object4得重现更新这个映射,我们仅仅须要简单的沿顺时针方向找到下一个可用的cache,在这里是cacheC就可以。如图,而不必所以映射关系全盘修改。
cacheB崩溃
当将cacheD增加在object2和object3之间时,B和D之间的object也需又一次映射。在这里object2将绑定到新增加的cacheD上。
例如以下图:
添加cacheD
----------------------------------------------------------------------------------------
八、虚节点
上述情况可以非常好的解决添加删除server节点时对整个系统大动干戈的问题。但还存在一个问题,那就是假设环形哈希空间上的cache较少的话,object的部署不会那么均匀。于是我们引入虚节点的概念。
。它可以比較好的改善这一缺点。 虚节点是环形哈希空间上哥cache点的副本,每一个cache关联着几个环形上的虚节点,当我们添加一个节点时,这意味着我们实际上在环形空间上添加了几个这种虚节点,相同删除某一cache时。我们也将移除环形空间上
全部和它相关的虚节点。
继续考虑上面的样例,如今系统中有cachA和cacheC,引入虚节点。并如果各存在2份,于是在环形空间上。一起有4个虚节点。cacheA1和cacheA2代表cacheA。还有cacheC1和cacheC2代表C。例如以下图:于是,如今从object到虚节点的映射为:
object1——>cacheA2;
object2——>cacheA1;
object3——>cacheC1;
object4——>cacheC2
这样分配就会显得相对均匀。例如以下图:
九、一致性哈希算法的应用
最后来个实际应用的样例。问题描写叙述: 比如手机朋友网有n个server,为了方便用户的訪问会在server上缓存数据,因此用户每次訪问的时候最好能保持同一台server。
已有的做法是依据ServerIPIndex[QQNUM%n]得到请求的server,这样的方法非常方便将用户分到不同的server上去。可是假设一台server死掉了,那么n就变为了n-1,那么ServerIPIndex[QQNUM%n]与ServerIPIndex[QQNUM%(n-1)]基本上都不一样了,所以大多数用户的请求都会转到其它server,这样会发生大量訪问错误。
问: 怎样改进或者换一种方法,使得:
(1)一台server死掉后。不会造成大面积的訪问错误,
(2)原有的訪问基本还是停留在同一台server上。
(3)尽量考虑负载均衡。
显然,传统的办法题目已经给出,即用模余方法:做法非常easy。但存在非常多问题不满足需求。于是我们考虑一致性哈希算法。正如前面所述。
其他应用场景:
在做server负载均衡时候可供选择的负载均衡的算法有非常多,包含: 轮循算法(Round Robin)、哈希算法(HASH)、最少连接算法(Least Connection)、响应速度算法(Response Time)、加权法(Weighted )等。当中哈希算法是最为经常使用的算法.
最典型的应用场景就是: 有N台server提供缓存服务,须要对server进行负载均衡。将请求平均分发到每台server上,每台机器负责1/N的服务。
经常使用的算法是对hash结果取余数 (hash() mod N):对机器编号从0到N-1,依照自己定义的hash()算法,对每一个请求的hash()值按N取模,得到余数i。然后将请求分发到编号为i的机器。
但这种算法方法存在致命问题。假设某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时须要将当掉的server从算法从去除,此时候会有(N-1)/N的server的缓存数据须要又一次进行计算。假设新增一台机器,会有N /(N+1)的server的缓存数据须要进行又一次计算。对于系统而言,这一般是不可接受的颠簸(由于这意味着大量缓存的失效或者数据须要转移)。那么,怎样设计一个负载均衡策略,使得受到影响的请求尽可能的少呢?
在Memcached、Key-Value Store、Bittorrent DHT、LVS中都採用了Consistent Hashing算法,能够说Consistent Hashing 是分布式系统负载均衡的首选算法。
版权声明:本文博客原创文章,博客,未经同意,不得转载。