在了解一致性哈希算法以前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景以后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优势,那么,咱们先来描述一下这个经典的分布式缓存的应用场景。
场景描述
假设,咱们有三台缓存服务器,用于缓存图片,咱们为这三台缓存服务器编号为0号、1号、2号,如今,有3万张图片须要缓存,咱们但愿这些图片被均匀的缓存到这3台服务器上,以便它们可以分摊缓存的压力。也就是说,咱们但愿每台服务器可以缓存1万张左右的图片,那么,咱们应该怎样作呢?若是咱们没有任何规律的将3万张图片平均的缓存在3台服务器上,能够知足咱们的要求吗?能够!可是若是这样作,当咱们须要访问某个缓存项时,则须要遍历3台缓存服务器,从3万个缓存项中找到咱们须要访问的缓存,遍历的过程效率过低,时间太长,当咱们找到须要访问的缓存项时,时长多是不能被接受的,也就失去了缓存的意义,缓存的目的就是提升速度,改善用户体验,减轻后端服务器压力,若是每次访问一个缓存项都须要遍历全部缓存服务器的全部缓存项,想一想就以为很累,那么,咱们该怎么办呢?原始的作法是对缓存项的键进行哈希,将hash后的结果对缓存服务器的数量进行取模操做,经过取模后的结果,决定缓存项将会缓存在哪一台服务器上,这样说可能不太容易理解,咱们举例说明,仍然以刚才描述的场景为例,假设咱们使用图片名称做为访问图片的key,假设图片名称是不重复的,那么,咱们可使用以下公式,计算出图片应该存放在哪台服务器上。
hash(图片名称)% N
由于图片的名称是不重复的,因此,当咱们对同一个图片名称作相同的哈希计算时,得出的结果应该是不变的,若是咱们有3台服务器,使用哈希后的结果对3求余,那么余数必定是0、1或者2,没错,正好与咱们以前的服务器编号相同,若是求余的结果为0, 咱们就把当前图片名称对应的图片缓存在0号服务器上,若是余数为1,就把当前图片名对应的图片缓存在1号服务器上,若是余数为2,同理,那么,当咱们访问任意一个图片的时候,只要再次对图片名称进行上述运算,便可得出对应的图片应该存放在哪一台缓存服务器上,咱们只要在这一台服务器上查找图片便可,若是图片在对应的服务器上不存在,则证实对应的图片没有被缓存,也不用再去遍历其余缓存服务器了,经过这样的方法,便可将3万张图片随机的分布到3台缓存服务器上了,并且下次访问某张图片时,直接可以判断出该图片应该存在于哪台缓存服务器上,这样就能知足咱们的需求了,咱们暂时称上述算法为HASH算法或者取模算法,取模算法的过程能够用下图表示。
可是,使用上述HASH算法进行缓存时,会出现一些缺陷,试想一下,若是3台缓存服务器已经不能知足咱们的缓存需求,那么咱们应该怎么作呢?没错,很简单,多增长两台缓存服务器不就好了,假设,咱们增长了一台缓存服务器,那么缓存服务器的数量就由3台变成了4台,此时,若是仍然使用上述方法对同一张图片进行缓存,那么这张图片所在的服务器编号一定与原来3台服务器时所在的服务器编号不一样,由于除数由3变为了4,被除数不变的状况下,余数确定不一样,这种状况带来的结果就是当服务器数量变更时,全部缓存的位置都要发生改变,换句话说,当服务器数量发生改变时,全部缓存在必定时间内是失效的,当应用没法从缓存中获取数据时,则会向后端服务器请求数据,同理,假设3台缓存中忽然有一台缓存服务器出现了故障,没法进行缓存,那么咱们则须要将故障机器移除,可是若是移除了一台缓存服务器,那么缓存服务器数量从3台变为2台,若是想要访问一张图片,这张图片的缓存位置一定会发生改变,之前缓存的图片也会失去缓存的做用与意义,因为大量缓存在同一时间失效,形成了缓存的雪崩,此时前端缓存已经没法起到承担部分压力的做用,后端服务器将会承受巨大的压力,整个系统颇有可能被压垮,因此,咱们应该想办法不让这种状况发生,可是因为上述HASH算法自己的缘故,使用取模法进行缓存时,这种状况是没法避免的,为了解决这些问题,一致性哈希算法诞生了。
咱们来回顾一下使用上述算法会出现的问题。
问题1:当缓存服务器数量发生变化时,会引发缓存的雪崩,可能会引发总体系统压力过大而崩溃(大量缓存同一时间失效)。
问题2:当缓存服务器数量发生变化时,几乎全部缓存的位置都会发生改变,怎样才能尽可能减小受影响的缓存呢?
其实,上面两个问题是一个问题,那么,一致性哈希算法可以解决上述问题吗?
咱们如今就来了解一下一致性哈希算法。
一致性哈希算法的基本概念
其实,一致性哈希算法也是使用取模的方法,只是,刚才描述的取模法是对服务器的数量进行取模,而一致性哈希算法是对2^32取模,什么意思呢?咱们慢慢聊。
首先,咱们把二的三十二次方想象成一个圆,就像钟表同样,钟表的圆能够理解成由60个点组成的圆,而此处咱们把这个圆想象成由2^32个点组成的圆,示意图以下:
圆环的正上方的点表明0,0点右侧的第一个点表明1,以此类推,二、三、四、五、6……直到2^32-1,也就是说0点左侧的第一个点表明2^32-1
咱们把这个由2的32次方个点组成的圆环称为hash环。
那么,一致性哈希算法与上图中的圆环有什么关系呢?咱们继续聊,仍然以以前描述的场景为例,假设咱们有3台缓存服务器,服务器A、服务器B、服务器C,那么,在生产环境中,这三台服务器确定有本身的IP地址,咱们使用它们各自的IP地址进行哈希计算,使用哈希后的结果对2^32取模,可使用以下公式示意。
hash(服务器A的IP地址) % 2^32
经过上述公式算出的结果必定是一个0到2^32-1之间的一个整数,咱们就用算出的这个整数,表明服务器A,既然这个整数确定处于0到2^32-1之间,那么,上图中的hash环上一定有一个点与这个整数对应,而咱们刚才已经说明,使用这个整数表明服务器A,那么,服务器A就能够映射到这个环上,用下图示意
同理,服务器B与服务器C也能够经过相同的方法映射到上图中的hash环中
hash(服务器B的IP地址) % 2^32
hash(服务器C的IP地址) % 2^32
经过上述方法,能够将服务器B与服务器C映射到上图中的hash环上,示意图以下
假设3台服务器映射到hash环上之后如上图所示(固然,这是理想的状况,咱们慢慢聊)。
好了,到目前为止,咱们已经把缓存服务器与hash环联系在了一块儿,咱们经过上述方法,把缓存服务器映射到了hash环上,那么使用一样的方法,咱们也能够将须要缓存的对象映射到hash环上。
假设,咱们须要使用缓存服务器缓存图片,并且咱们仍然使用图片的名称做为找到图片的key,那么咱们使用以下公式能够将图片映射到上图中的hash环上。
hash(图片名称) % 2^32
映射后的示意图以下,下图中的橘黄色圆形表示图片
好了,如今服务器与图片都被映射到了hash环上,那么上图中的这个图片到底应该被缓存到哪一台服务器上呢?上图中的图片将会被缓存到服务器A上,为何呢?由于从图片的位置开始,沿顺时针方向遇到的第一个服务器就是A服务器,因此,上图中的图片将会被缓存到服务器A上,以下图所示。
没错,一致性哈希算法就是经过这种方法,判断一个对象应该被缓存到哪台服务器上的,将缓存服务器与被缓存对象都映射到hash环上之后,从被缓存对象的位置出发,沿顺时针方向遇到的第一个服务器,就是当前对象将要缓存于的服务器,因为被缓存对象与服务器hash后的值是固定的,因此,在服务器不变的状况下,一张图片一定会被缓存到固定的服务器上,那么,当下次想要访问这张图片时,只要再次使用相同的算法进行计算,便可算出这个图片被缓存在哪一个服务器上,直接去对应的服务器查找对应的图片便可。
刚才的示例只使用了一张图片进行演示,假设有四张图片须要缓存,示意图以下
1号、2号图片将会被缓存到服务器A上,3号图片将会被缓存到服务器B上,4号图片将会被缓存到服务器C上。
一致性哈希算法的优势
通过上述描述,我想兄弟你应该已经明白了一致性哈希算法的原理了,可是话说回来,一致性哈希算法可以解决以前出现的问题吗,咱们说过,若是简单的对服务器数量进行取模,那么当服务器数量发生变化时,会产生缓存的雪崩,从而颇有可能致使系统崩溃,那么使用一致性哈希算法,可以避免这个问题吗?咱们来模拟一遍,便可获得答案。
假设,服务器B出现了故障,咱们如今须要将服务器B移除,那么,咱们将上图中的服务器B从hash环上移除便可,移除服务器B之后示意图以下。
在服务器B未移除时,图片3应该被缓存到服务器B中,但是当服务器B移除之后,按照以前描述的一致性哈希算法的规则,图片3应该被缓存到服务器C中,由于从图片3的位置出发,沿顺时针方向遇到的第一个缓存服务器节点就是服务器C,也就是说,若是服务器B出现故障被移除时,图片3的缓存位置会发生改变
可是,图片4仍然会被缓存到服务器C中,图片1与图片2仍然会被缓存到服务器A中,这与服务器B移除以前并无任何区别,这就是一致性哈希算法的优势,若是使用以前的hash算法,服务器数量发生改变时,全部服务器的全部缓存在同一时间失效了,而使用一致性哈希算法时,服务器的数量若是发生改变,并非全部缓存都会失效,而是只有部分缓存会失效,前端的缓存仍然能分担整个系统的压力,而不至于全部压力都在同一时间集中到后端服务器上。
这就是一致性哈希算法所体现出的优势。
hash环的偏斜
在介绍一致性哈希的概念时,咱们理想化的将3台服务器均匀的映射到了hash环上,以下图所示
可是,理想很丰满,现实很骨感,咱们想象的与实际状况每每不同。
在实际的映射中,服务器可能会被映射成以下模样。
聪明如你必定想到了,若是服务器被映射成上图中的模样,那么被缓存的对象颇有可能大部分集中缓存在某一台服务器上,以下图所示。
上图中,1号、2号、3号、4号、6号图片均被缓存在了服务器A上,只有5号图片被缓存在了服务器B上,服务器C上甚至没有缓存任何图片,若是出现上图中的状况,A、B、C三台服务器并无被合理的平均的充分利用,缓存分布的极度不均匀,并且,若是此时服务器A出现故障,那么失效缓存的数量也将达到最大值,在极端状况下,仍然有可能引发系统的崩溃,上图中的状况则被称之为hash环的偏斜,那么,咱们应该怎样防止hash环的偏斜呢?一致性hash算法中使用"虚拟节点"解决了这个问题,咱们继续聊。
虚拟节点
话接上文,因为咱们只有3台服务器,当咱们把服务器映射到hash环上的时候,颇有可能出现hash环偏斜的状况,当hash环偏斜之后,缓存每每会极度不均衡的分布在各服务器上,聪明如你必定已经想到了,若是想要均衡的将缓存分布到3台服务器上,最好能让这3台服务器尽可能多的、均匀的出如今hash环上,可是,真实的服务器资源只有3台,咱们怎样凭空的让它们多起来呢,没错,就是凭空的让服务器节点多起来,既然没有多余的真正的物理服务器节点,咱们就只能将现有的物理节点经过虚拟的方法复制出来,这些由实际节点虚拟复制而来的节点被称为"虚拟节点"。加入虚拟节点之后的hash环以下。
"虚拟节点"是"实际节点"(实际的物理服务器)在hash环上的复制品,一个实际节点能够对应多个虚拟节点。
从上图能够看出,A、B、C三台服务器分别虚拟出了一个虚拟节点,固然,若是你须要,也能够虚拟出更多的虚拟节点。引入虚拟节点的概念后,缓存的分布就均衡多了,上图中,1号、3号图片被缓存在服务器A中,5号、4号图片被缓存在服务器B中,6号、2号图片被缓存在服务器C中,若是你还不放心,能够虚拟出更多的虚拟节点,以便减少hash环偏斜所带来的影响,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的几率就越大。
前端