一致性hash(consistent hashing)

来源:互联网 发布:张曼菱 知乎 编辑:程序博客网 时间:2024/06/01 09:04

使用的基本场景:

假设现在有N个服务器,如何将一个对象映射到这个N个服务器上呢,一般我们都会想到这样的方法:hash(object)%N来计算对象的Hash值,然后来均匀的映射到这N个服务器上,如你所想,一切运行正常,但是,现在可能会出现这样的两种情况:

  1. 很不幸,一台服务器由于承受不住压力,宕机了,怎么办,问题大了,现在映射到这个服务器上的所有的对象都将会失效,而且也需要立马把它从服务器集群中移除,那么,现在这个时候,服务器编程了N-1台,对应的hash映射公式也就需要改变为:hash(object)%(N-1);
  2. 由于这个创意很受人欢迎,访问人数剧增,访问量加重了,现有的服务器已经支撑不了了,怎么办,最简单有效的方法,加服务器,这个时候服务器的集群有N+1台服务器,那么相应的映射的公式也就变成了hash(object)%(N+1)

对于1和2的这两种情况,影响的范围是非常广的,它们意味着在突然之间,几乎所有的服务器都会失效,而对于服务器来说,这就是一场巨大的灾难,所有的访问量在那一刹那都将会直接冲向后台服务器。

我们再考虑下面一种情况:由于硬件的能力越来越强,你可能会有这样一种想法,我能不能让其中的某一台服务器多做点儿事呢,这个时候普通的hash算法是明显做不到的。但是有一种算法可以做到:一致性Hash。接下来就详细介绍一下一致性Hash算法。


一致性hash算法:
一致性Hash算法在1997年由麻省理工学院提出,它是一种分布式Hash(DHT)实现算法,设计的目标是为了解决在新特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性hash算法修正了CARP使用简单的hash算法带来的问题,使得分布式hash可以在P2P环境中真正的得到应用。

一致性hash算法提出了在动态变化的服务器的环境中,判断hash算法的好坏的四个定义:

  1. 平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件;
  2. 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区;
  3. 分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性;
  4. 负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷

接下来我们说说一致性hash算法到底是如何来设计的:

环形Hash空间
按照常用的hash算法,我们将对应的key映射到一个有2^32这么多的空间中,也就是编号从0~2^32-1的空间中,现在我们可以这样,将这些数字头尾相连,这就是一个闭合的环形。如下图所示:这里写图片描述

把对象映射到hash空间
比如现在有四个对象object1,object2,object3,object4,我们通过特定的hash函数计算出对应的key的值,然后再散列到hash环上:

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

把机器映射到hash空间
在采用一致性hash算法的分布式集群将新的机器假如,其原理是通过使用与对象存储一样的hash算法将机器也映射到hash换上,需要注意的是:一般的情况下对机器的hash计算是采用机器的IP或者是机器唯一的别名作为输入值,然后以顺时针的方向计算,将所有的对象存储到距离自己最近的机器中。
假设现在有node1,node2,node3三台机器,通过hash算法计算出对应的key值,映射到hash环中:

Hash(node1) = KEY1;Hash(node2) = KEY2;Hash(node3) = KEY3;

现在hash环是这样的:
这里写图片描述

通过上图可以看出对象与机器处于同一个hash空间中了,这样,按照顺时针转动object1存储到NODE1中,object3存储到NODE2中,object2和object4存储到NODE3中,在这样的部署环境中,hash环是不会变更的,那么,通过算出对象的hash值就能快速的定位到对应的机器中,这样就能找到对象真正的存储位置了。

机器的删除和添加
普通的hash求余算法一个非常大的弊端就是在有机器的添加或者删除之后就会造成大量的存储位置失效了,这样就满足不了单调性了,下面我们来看看一致性hash算法是如何来处理的。

  • 节点添加:
    如果往集群中添加一个新的节点node4,通过对应的hash算法得到KEY4,并映射到环中,如下图所示:
    这里写图片描述

通过顺时针迁移的规则,object2就被迁移到NODE4中,其他的对象还保持原有的存储位置。
- 节点删除:
如上图所示,假如现在NODE2出现故障被删除了,那么按照顺时针迁移的方法,object3将会迁移到NODE4中,这样仅仅object3的映射位置发生了变化,其他的对象没有任何的改动。

平衡性
说完上面这些,接下来该说说平衡性了,虽然一致性hash满足了单调性和负载均衡的特性以及一般hash算法的分散性,但是这并不能当做其被广泛应用的原由,因为还缺少了一个很重要的东西,那就是平衡性,下面将分析一致性hash是如何满足平衡性的。
比如只部署了NODE1和NODE3的情况,object1被存储到NODE1中,而object2,object3和object4都被存储到NODE3中,这样就造成了不平衡的状态,在一致性hash算法中,为了尽可能的满足平衡性,引入了虚拟节点。

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

现在我们以只部署了NODE1和NODE3的情况为例。之前对象再机器上的分布很不均衡,现在我们以两个副本为例,这样整个hash环中就存在四个虚拟节点,最后的映射关系就如下图了:
这里写图片描述
根据上图我们可以知道映射关系了:

object1->NODE1-1,object2->NODE1-2,object3->NODE3-2,object4->NODE3-1

通过虚拟节点的引入,对象的分布就比较均衡了,那么在实际的操作中,真正的对象查询是如何工作的呢?对象是怎么从hash到虚拟节点到实际节点转换的呢?接下来我们来看一个转换图:
这里写图片描述

虚拟节点的hash计算可以采用对应节点的IP地址加数字后缀的方式,举个例子:
比如NODE1的IP地址为192.168.1.100,引入虚拟节点钱,计算A的hash值为:Hash(192.168.1.100),引入虚拟节点后,计算虚拟节点1和2的hash值为:Hash(192.168.1.100#1)和Hash(192.168.1.100#2)。

1 0
原创粉丝点击