本系列为网上收集转载分布式相关知识点系列文章,并非原创。如果侵权,请联系我删除!!!
在了解hash算法之前先去了解一下缓存中的一个应用场景,再来理解一致性hash算法就会简单很多。
场景描述
假设,公司有三台缓存服务器,用于缓存图片,这三台缓存服务器的编号为0号,1号,2号,现在有三万张图片需要缓存,希望这么多的缓存数据能均匀的缓存到3台服务请求上去(也就是说每台机器上平均分配1万左右的数据),以便能够分摊缓存的压力。
此处的问题:
那么应该怎么做?如果我们没有任何规律的将3万张图片平均的缓存到3台服务器上,可不可以满足需求?
答案:
可以!但是如果这么做,当我们需要访问某个和缓存选项时,则需要遍历3台服务器,从这3万个缓存项中找到需要访问的缓存,整个遍历下来的过程时间长,效率低,这样也就失去了缓存存在的价值和意义,缓存存在的目的就是提高速度,改善用户的体验,减轻后端服务器的压力,如果每次都要遍历整个缓存集群服务器,不用想都能累死.
好一点的做法:
原始的做法是对缓存项的键进行hash,将hash后的结果对缓存服务器数量进行取模操作,通过取模后结果,决定缓存项要缓存在那一台服务器上,举例说明:假设以我们使用的图片名称作为访问图片的key,假设图片名称不能重复,那么我们可以使用以下公式,及计算出图片应该存放在那台服务器上。
公式:hash(图片名称)% N
因为图片名称是不能重复的,所以,当我们对同一个图片名称做相同的hash运算时得出的结果应该是不变的,如果有3台服务器,使用hash运算后的结果对3取余,那么余数也一定是0、1、2,正号跟之前服务器编号相同,如果求余的结果为1,就把图片名称对应的图片缓存在1号服务器上,2就和缓存到2号服务器上,那么当我们访问任意一个图片的时候,只要再次对图片名称进行运算即可得出对应的图片应该被放在那台缓存服务器上,只需要去对应的服务器上去查找即可,如果图片在对应的服务器上不存在,证明没有被缓存(接着会把这张图片缓存在这个服务器上),也不用再去遍历其他缓存服务器了。通过此方法可以把3万图片随机分布到3台缓存服务器上,下次访问某张图片时,直接能够判断出该图片应该存放在那台缓存服务器上了。可以把上述算法称为:HASH算法还活着是取模算法
以下是运算原理图:
但是使用上述hash算法进行缓存时会出现一些缺陷,如果3台并不能满足我们的需求时那么应该怎么做?肯定是增加几台服务器就可以了,假设我们增加1台服务器,服务器的数量由3变成了4,此时仍然用上述方法对同一张图片进行缓存,那么这张图片所在的服务器的编号必定是与原来的3台服务器所在的 编号是不同的,因为除数3变成了4,被除数不变的情况下,余数肯定不同,这情况带来的结果就是当服务器数量变动时,所有和缓存的位置都要发生改变,也就是说缓存服务器数量发生改变时,所有缓存数据在一定时间是失效的,当应用无法从缓存中和获取数据时,则会向后端服务器请求数据,同理,如果3台缓存服务器中突然有一台出现了故障,,无法进行缓存数据,那么需要移除故障机器,但是如果移除了一台缓存服务器后,数量从3变成了2,如果想访问有一张图片,这张图片缓存为位置必定发生改变,以前缓存的图片也会失去缓存的作用和意义,由于==**`大量缓存在同一时间失效,造成了缓存的雪崩(血崩),此时前端缓存已经无法起到成端部分压力的作用,后端服务器将会承担所有巨大压力,会导致整个系统可能会被压垮,==所以为了避免这类情况的发生,一致性hash算法诞生了!
综合一下上述出现的问题:
第一个问题:
当缓存服务器数量发生变化时,会引起缓存的血崩,可能引起整体系统压力过大而崩溃(大量缓存同一时间失效),
第二个问题:
当缓存服务器数量发生变化时,几乎所有缓存的数据都会发生改变,怎么才能尽量减少受影响的缓存?
一致性hash算法概念
其实一致性hash算法也是取模运算,只是,上面描述的取模算法是对服务器数量进行取模,而一致性hash是对2^32取模
插入小知识:
为什么非得对2^32取余?
对232取余是有依据的IPV4最大数量就是232,所以这样就能保证所有的ip的地址进行取余时不会重复—对应hash环上面的正数。
首先把2^32想象成有一个大大的圆,就像钟表上由60个点组成的圆,如图:
由2^32个点组成的圆环称为hash环。
圆环的正上方代表0,0点右侧第一个1以此类推2,3,4,5,6……一直到最后2^32-1。
假设我们有3台缓存服务器,服务器A,B,C,然后用他们各自的IP地址进行hash计算,使用hash后的结果改过对2^32取模。hash(服务器A的IP地址) % 2^32
通过上面的计算结果一定是一个0到232-1之间的一个整数,我们就用这个整数代表服务器A,既然这个整数肯定处于0到232-1之间,那么必定与hash环的上一个点与这个整数对应。刚才已经说明,使用这个整数代表服务器A,那么服务器A就可以映射到这个环上。
同理服务器B和C也是相同的方法映射到hash环中
计算公式:
hash(服务器B的IP地址) % 2^32
hash(服务器C的IP地址) % 2^32
算出结果然后将服务器映射到hash环上去。如图所示:
假设3台服务器均匀到映射到hash环上,(这是理想得到情况)
假设,我们需要使用缓存服务器缓存图片
而且仍然使用图片的名称作为找到图片的Key,那么我们使用以下公式可以将图片映射到上图中的hash环上。
hash(图片名称) % 2^32
映射后得到的结果如下图:(红点点代表图片)
那么问题来了,图片和服务器都被映射到了hash环上,那么上图的图片到底应该缓存到那一台缓存服务器上呢?
答:
应该被缓存到A服务器上,为什么?
因为从图片开始的位置开始,沿着顺时针方向遇到的第一个服务器就是A服务器,所以会被缓存到A服务器上。如下图:
一致性hash算法说明:
通过一致性hash算法这种方法,判断一个对象应该被缓存到那台服务器上的,将缓存服务器与被缓存对象都映射到hash环上以后,从被缓存对象的位置出发,沿着顺时针方向遇到的第一个服务器,就是当前对象将要缓存的服务器,由于被缓存对象与服务器hash后的值都是固定的,所以服务器不变的情况下,一张图片必定会被缓存到固定的服务器上,那么,当下次访问这张图片时,只要再次使用相同的算法进行计算,即可算出这张图片被缓存在那个服务器上,直接去对应的服务器上查找即可。
假设有4张图片需要缓存,如下图:
1、2图片缓存到A上,3缓存到B上,4缓存到C
一致性hash算法的优点
一致性hash算法能够解决之前出现的问题吗?如果进行简单的取模,当服务器数量发生变化时,会产生缓存的雪崩,从而导致系统崩溃,那么用一致性hash能够避免这个问题吗?
继续模拟测试实验!!
假设服务器B出现故障,需要移除B,如下图:
在服务器B没有移除时,图片3应该缓存到B中,可是移除后,按照一致性hash算法是规则,3应该被缓存到C中,因为从3的位置顺时针除法遇到的第一个服务器节点是C,也就是说如果B出现故障移除后,3的缓存位置发生改变。
这里的一致性hash算法的优点就体现出来了!
图片4依然会被缓存到C中,图片1和2突然缓存到A中,与移除B之前没有任何区别,这就是优点!
1)如果使用hash算法:当服务器数量发生改变时,所有的服务器缓存在同一时间失效了
2)而使用一致性hash算法:服务器数量发生改变时,并不是所有都会失效,而只有部分缓存失效,前端的缓存仍然能分担整个系统的压力,而不至于所有压力都在同一个时间集中到后端服务器上。
hash环偏斜问题
在最开始介绍概念年的时候,说的是在理想的情况下是**均匀的分布到hash环上!**如下图
但是理想是很丰满的,我们想象的和现实往往是不同的!
在实际映射中,服务器可能会被映射成一下模样
那么被缓存的对象很有可能大部分集中缓存到某一台服务器上如下图:
图上2、3、4、5、6、都很会存储到A服务器上,1会存到B上,C服务器一张也没有,三台服务器并没有合理的平均充分的利用,缓存分布极度不均匀,重要的是如果A出现故障,会导致失效的数量也将达到最大值。在极端的情况下依然会出现系统崩溃的问题!
以上情况被称之为hash环偏斜,那么如何防止hash环偏斜呢?
虚拟节点
“虚拟节点”是“实际节点”(实际的物理服务器)在hash环上的复制品,一个实际节点可以对应多个虚拟节点。如下图:
ABC三台服务分别虚拟出一个虚拟节点(也可以虚拟出更多的虚拟节点)引入虚拟节点的概念后,缓存的分布就均衡的多了,上图中3、4、5、的图片都被放到了节点A中如果不放心可以虚拟出更多的虚拟节点,以便减少hash环偏斜所带来的影响,虚拟节点越多,hash环上的节点就越多,缓存均匀分布的概率就越大!!!
————————————————
版权声明:本文为CSDN博主「GeekShuai」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34672033/java/article/details/88916789