一致性哈希是一种特殊的哈希算法。使用一致性哈希,在添加或删除一个槽位时,平均只需要对K/n个关键字重新映射,其中K是关键字数量,n是槽位数量。然而在传统的哈希算法中,哈希表槽位数(大小)的变化几乎需要对所有关键字进行重新映射。
需求场景
在使用n台缓存服务器时,对资源o的请求使用hash(o) = o mod n来映射到某一台缓存服务器。当增加或减少一台缓存服务器时这种方式可能会改变所有请求资源o对应的hash值,也就是所有的缓存都失效了,这会使得缓存服务器大量集中地向原始内容服务器更新缓存。因此,需要一致性哈希算法。
一致性哈希尽可能使同一个资源映射到同一台服务器,在增加一台服务器时,新服务器分担原有服务器的一些请求,在减少一台服务器时,剩余服务器分担这台减少的服务器的请求。
特性
一致性 hash 算法提出了在动态变化的 Cache 环境中,判定哈希算法好坏的四个定义:
平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
一致性哈希原理
环形hash空间。通常的哈希算法都是把value映射到一个32位的key值,可就是0~2^32-1的数值空间;想象将所有的key值数轴弯曲,形成如下圆环:
把资源映射到环上。考虑把需求场景说到的请求资源映射到环上,即简单的通过一个hash算法计算出一个hash值,如下:
把缓存服务器映射到环上。使用相同的hash算法,再把我们的缓存服务器映射到环上,如下图的蓝色圆圈:
把资源映射到服务器上。现在资源和服务器都映射到了环上,接下来考虑的就是如何将资源映射到服务器上。简单的考虑,资源对应的点,沿着顺时针方向遇见的第一个服务器对应的点,就是该资源映射的服务器。
考虑服务器增减。一致性哈希最终解决的问题是:“一致性哈希尽可能使同一个资源映射到同一台服务器,在增加一台服务器时,新服务器分担原有服务器的一些请求,在减少一台服务器时,剩余服务器分担这台减少的服务器的请求”。来看下是否满足:
增加一台服务器,入下图Sever4。相当于把映射到Sever1的数值空间切断了,一半映射到了Server4。
减少一台服务器,如把Sever3干掉。这样原本映射到Server3的数值空间合并到了Server1的数值空间里。
以上考虑的确满足了基本需求,但是平衡性受到很大破坏:增加Server4节点时,只分担了Server1的请求量,这样,Server1、Server4的请求量只是Server2、Server3的一半!减少Server3节点时,只是Server1抗住了这部分请求,这样,Server1的请求量是Server2的两倍!完全不平衡。
虚拟节点。考虑到刚才说到的平衡性,一致性哈希算法映入了“虚拟节点”(如下图黄色圆圈)。把资源和虚拟节点映射到数值空间环上,再把“虚拟节点”分配给服务器。
再考虑减少节点的情况,例如干掉Server3,Server3被干掉,实际的虚拟节点仍存在,原本分配给Server3的虚拟节点就可以“均匀的”分配给Sever1、Server2。相应的,增加节点也类似(实际使用中,虚拟节点比例子中多)。
本文暂时没有评论,来添加一个吧(●'◡'●)