就够了

渭城朝雨浥轻尘 渭城朝雨浥轻尘     2022-12-03     667

关键词:


        在理解一致性hash算法之前,让我们来看一个在缓存中最经典的案例场景,理解这个运用场景之后,我们再去理解一致性hash算法就更容易了,在这个过程中我们还能体会一致性hash算法的优势之处,好了,让我们来描述一下这个经典的分布式缓存案例。

        1、场景描述

        假设,我们有三台缓存服务器用来缓存图片,我们给这三台服务器一次命名为服务器1、服务器2、服务器3,然后呢,我们有3万张图片需要缓存,我们希望这3万张图片均匀的分布在这三台服务器上,这样就可以让三台服务器均摊压力。换句话说,就是想每台服务器上存一万张图片,我们该怎么做呢?如果我们没有任何的规则限制,我们可以将三张图片依次落在3台服务器上(从第一张图片开始放的是1,下一张就放2,再下一张就放3,一直这样循环,直到把3万张图片放完),这样能满足要求吗,当然可以!但是如果你这样做,当你需要读取图片时,你需要遍历三个服务器,每次都要遍历一万次,直到找到为止,这个过程效率就太低了~时间太长了,当我们命中缓存的时间过长,以至于不能满足要求,这个缓存动作就失去意义了,缓存的目的是为了提高查询效率,改善用户体验,减轻后端服务的压力;如果每次查询数据时都要遍历所有的服务器上的所以缓存,想到这个就心累,那我们该怎么办呢?

        最先想到的方法是通过对关键字进行hash取值,然后将hash值对服务器数量进行取模,通过取模的结果决定将这个缓存项放到哪个服务器,这个可能不太那么好理解,举个栗子,我们还是以上面那个存图片的场景为例,假设我用图片名作为key来定位图片,假设图片名不重复,我们通过以下公司,就可以推断这个图片将被放置到哪个服务器上。

hash( Image name )% N

         因为图片名称是不重复的,所以当我们通过相同的hash运算,同一张图片名称得到的结果是一样的,如果我们有三台服务器,我们将hash值对3求余,那么余数必然是0、1、2,是吧,这不就对应到了我们的服务器编号了!如果余数是0,这对应的缓存项就落在0号服务器上,如果余数是1,对应的缓存项就落在2号服务器上,如果余数是2,同理。

        这样我们访问任何一张图片时,只要再进行一下如上运算,就能知道我们要访问的图片在哪个服务器上,我们只需要直接去那个服务器上找,如果对应的服务器上没有,就说明这个图片还没有被缓存,无需再去其他服务器上找了,这样的话,我们就能将3万张图片随机放到3台服务器上,当我们需要访问时,也能直接定位图片应该在哪个服务器上,为了便于理解,我们将上诉方法称之为HASH算法或者叫取模算法,这个取模算法的流程如下所示:

了解一致性hash原理,这篇文章就够了_缓存

         但是呢,我们使用这个HASH算法在缓存场景时,会有些问题,试想一下,如果这三台缓存服务器不足以支撑我们的缓存业务,我们该怎么办?是的没错,这个很简单,再加两台不就完事了!假设我们添加一台缓存服务器,那么问题来了,之前我们求余的基数是3,现在变成了4;如果我们使用相同的方法缓存相同的图片,在增加服务器前后,相同的图片应该对应相同的缓存服务器,但现在肯定不同了,因为除数由3变成了4,除数变了,余数肯定也变了;这样造成的结果就是,当服务器数量发生变化时,所有的缓存位置都必须更改((也就是根据新的服务器数量进行重新定位)),换句话说,当服务器数量发生改变时,所有的缓存将在一定时间内不可用(在重新计算位置)。当运用程序无法从缓存中取到数据时,所有的请求都打到后台服务,同理,假设三台服务器中有一台挂了,无法缓存,我们需要将这台服务器移除,但如果你移除它,那可用服务器数量就有3台变成了2台,如果你想访问图片,这个时候图片的位置必然发生改变,由于大量的缓存同时失效,那缓存也就失去了意义,造成缓存雪崩。此时前端缓存已经无法承受访问压力,后端服务器将承担极大的压力,整个系统可能奔溃。

        因此,我们应该尽量避免这种情况的发生,但由于HASH算法的局限性,当我们用取模策略时,这个是不可避免的,为了解决这些问题,一致性哈希算法应运而生。

让我们总结一下上述算法存在的问题:

        问题1:当缓存服务器数量发生改变时,会导致缓存雪崩,可能会引起整个系统因压力过大而崩溃(大量缓存同时失效)

        问题2:当服务器数量发生改变时,几乎所有的缓存位置都要发生改变,怎么样才能将缓存的影响降到最低(原来的缓存位置不动)?

        实际上,上面两个问题是同一个问题,一致性hash就能解决上述问题。

        现在让我们来看看一致性hash算法。

2、均匀Hash的基本概念

        实际上,一致性哈希算法用的也是取模方法,只不过这个取模方法不是对服务器数量取模,而是对2^32取模,这是什么意思呢?听我娓娓道来。

        首先,让我们把2^32想象成一个圆,就像一个圆钟,钟上的圆可以看成由60个点组成的圆,而我们想象的这个圆可以看成由2^32个点组成的,示意图如下:

了解一致性hash原理,这篇文章就够了_服务器_02

         圆圈的正上方那个点表示0,第一个点的右边,一次是1、2、3、4、5、6……一直到2的32次方减一,我们把这个由2的32次方个点组成的东西称为哈希环。

        一致性hash算法与上述的环有什么关系呢?我们继续讨论,还是以上面的场景为例,假设我们有三台服务器,分别为服务器A、服务器B、服务器C,众所周知,在生产环境,这三台服务器必然有自己的IP地址,我们就它们各种的IP进行hash运算,将得到的结果与2^32进行取模,就像下面这个公式描述的一样:

 hash( The server A Of IP Address ) % 2^32

        经过上述公式的运算,结果必然落在0-2^32-1之间,我们把这个点在哈希环上的位置映射为服务器所在的位置,示意图如下:

了解一致性hash原理,这篇文章就够了_服务器_03

 同理,我们可以对服务器B、服务器C进行相同的hash映射

hash( The server B Of IP Address ) % 2^32

hash( The server C Of IP Address ) % 2^32

通过上述方法,我们可以将服务器B和服务C映射到哈希环上,示意图如下:

了解一致性hash原理,这篇文章就够了_缓存_04

现在假设三台服务器都映射到环上了,如上图所示(当然,这是最理想的情况,我们后面在讨论)

     

很好,截止到目前,我们使用上述方法,已经将缓存服务器映射到哈希环上了,我们也可以使用相同的方法,将需要缓存的对象映射到哈希环上。

假设我们需要用缓存服务器来缓存图片,还是通过图片名称来作为查询图片的key,我们可以使用相同的方法将图片映射到哈希环上。

 hash( Image name ) % 2^32

这个映射关系如下,环上橙色的点就是

了解一致性hash原理,这篇文章就够了_缓存_05

        很好,现在服务器和图片都映射到哈希环了,那我们的图片该缓存到哪个服务器上呢?上述图片将会被缓存在服务器A,这是为什么呢?因为从这个图片的位置开始,顺时针往前移动,最先遇到的点是服务器A,因此这个图片将缓存在服务器A上,如下图所示:

了解一致性hash原理,这篇文章就够了_算法_06

        明白了吧,这就是一致性哈希算法的原理,通过将服务器和待缓存对象都映射到哈希环上,从而确定待缓存对象应该缓存到哪个服务器上;从缓存对象在环上的位置开始算,顺时针方向第一个遇到的服务器,将缓存当前对象。由于被缓存对象和服务器hash后的值是固定的,因此,只要服务器数量不变,一个图片对应的缓存服务器也一定是固定不变的。随后如果你想获取这张图片,只需要同相同的算法再算一遍,就可以确定这张图片在哪个服务器上,直接到相应的服务器上去获取相应的图片。

        上面的栗子中只有一张图片,假设有四张图片,示意图如下:

了解一致性hash原理,这篇文章就够了_哈希算法_07

如上图所示,图片1、图片2将落在服务器A上,图片3将落在服务器B上,图书4将落在服务器C上。

3、一致性哈希算法的优势

        老铁们,通过上面的描述,我想你应该理解了一致性哈希算法的原理了,不过,一致性哈希算法怎么就解决了我们上面提到的两个问题呢?我们说过,如果我们只是简单的对服务器数量求余,当服务器数量发生改变时,将会发生缓存雪崩,进而很可能造成系统奔溃,那么我们用这个一致性哈希算法,这个问题就能避免吗?让我们再来推导一次,答案自会揭晓。

        假设,服务器B宕机了,现在我们需要将服务器B退役,我们将上图中的服务器B从哈希环中移除,示意图如下

了解一致性hash原理,这篇文章就够了_服务器_08

        在服务器B被移除前,图片3应该落在服务器B上的,现在服务器B下线了,根据一致性哈希算法的的顺时针法则,图片3将被缓存到服务器C上;因为从图片3的位置出发,顺时针走第一个遇到的服务器节点是C,简而言之,当服务器因宕机下线后,图片3的缓存位置将发生改变

了解一致性hash原理,这篇文章就够了_哈希算法_09

        但是呢,图片4仍然将缓存在服务器C上,图片1、图片2也将保持原位置不变缓存在服务器A上,它们的位置在服务器B移除前后都保持不变,这就是一致性哈希算法的优势。如果你使用之前的hash算法,当服务器数量发生变化时,所有的缓存都将失效,作为对比,如果你使用一致性哈希算法,同样是服务器数量发生变化,并不是所有的缓存都失效,仅仅是一部分缓存失效,前端的缓存将持续的承担流量压力,而不是将压力全部转移给后端服务。

这就是一致性哈希算法的优势。

4、分布不均的哈希环

        在介绍一致性哈希算法概念的时候,我们采用的是理想化的方式,假设3台服务器均匀的分布在哈希环上,如下图所示:

了解一致性hash原理,这篇文章就够了_服务器_10

        而在实际去情况中,映射关系有可能是这样的

了解一致性hash原理,这篇文章就够了_服务器_11

        这明眼人一眼就能看出,如果映射关系像上图那么的不均匀,一定会导致绝大部分的缓存都落在某个服务器上,如下图所示:

了解一致性hash原理,这篇文章就够了_数据结构_12

        如上图所示,图1、2、3、4、6都落在A服务器上,只有图片5落在服务器B上,服务器C干脆就没有任何图片在它上面,这就导致服务器A、B、C没有被合理的充分利用;缓存分布极其不均匀,如果此时服务器A出了问题,将导致大量的缓存不可用,在极端情况下,仍然有可能导致系统崩溃,上图所示的哈希环我们称之为——哈希倾斜,那我们该如何解决哈希倾斜问题呢?一致性哈希算法用“虚拟节点”的方式来解决这个问题,让我们接着往下看。

5、虚拟节点

        好了,我们继续。因为我们只有3台服务器,当我们把服务器映射到哈希环上时,很有可能就会发生哈希倾斜,一旦发生了哈希倾斜,服务器上的缓存将分布不均;那怎么行呢!如果你想将缓存均匀的分布在3台服务器上,你就必须使这三台服务器尽可能的均匀分布在哈希环上。然而,这真正的服务器只有三台,怎么才能让它们变得更多呢?英雄所见略同,我们就是要无中生有的变出更多的服务器节点。由于没有真实的服务节点,我们只能够通过虚拟方法来复制已存在的物理节点,这些通过实际节点复制而来的节点成为虚拟节点。虚拟节点加入哈希环后的示意图如下:

了解一致性hash原理,这篇文章就够了_算法_13

        虚拟节点在哈希环上可以看成是实际节点的副本,一个实际节点可以有多个虚拟节点。

        如上图所示,服务器A/B/C都可以有一个虚拟节点,当然了,如果你喜欢,你可以创建更多的虚拟节点。在引入虚拟节点之后,缓存分布更均匀了,如上图所示,图片1、3落在服务器A,图片4、5落在服务器B,图片2、6落在服务器C,如果还不放心,你可以创建更多的虚拟节点,以便减少哈希倾斜带来的影响,虚拟节点越多,哈希环上的节点就越多,缓存就分布的越均匀。

关于http协议,一篇就够了

关于HTTP协议,一篇就够了 作者 RaphetS 关注2016.10.1306:48* 字数4806 阅读12969评论26喜欢203赞赏1HTTP简介HTTP协议是HyperTextTransferProtocol(超文本传输协议)的缩写,是用于从万维网(WWW:WorldWideWeb)服务器传输超文本到... 查看详情

就够了

    在理解一致性hash算法之前,让我们来看一个在缓存中最经典的案例场景,理解这个运用场景之后,我们再去理解一致性hash算法就更容易了,在这个过程中我们还能体会一致性hash算法的优势之处,好了,让我们... 查看详情

就够了(珍藏版)(代码片段)

...学习目录二、扩展目录一、学习目录认识MongoDB一篇文章就够了Windows平台安装MongoDB教程Linux上安装MongoDBwindows安装MongoDB,Robo3T一篇文章带你搞定MongoDB的基本操作(增、删、改、查)MongoDB数据类型MongoDB文档更新操作Mong... 查看详情

handler原理剖析,看这篇就够了

Handler原理剖析,看这篇就够了本篇文章将会对Handler进行深层次的剖析,结合关系剖析图、代码走向剖析图以及10个常见问题,希望看完文章的同学都能有所收获,加深对Handler的了解!一、Handler运行原理剖析1.... 查看详情

真的,java并发编程入门看这个就够了(代码片段)

(真的,Java并发编程入门看这个就够了)1.Java天生多线程importjava.lang.management.ManagementFactory;importjava.lang.management.ThreadInfo;importjava.lang.management.ThreadMXBean;publicclassJavaThreadpublicstaticvoidmain(Str 查看详情

c语言这一张图就够了!!!

思维导图,原文件更详细,需要的私信我哦!!! 查看详情

就够了

按钮是一个在游戏中非常常用的组件,包含各种各样的形态和功能,今天就在这篇文章里汇总一下拥有各种不同点击效果的按钮,以及一些具有不同触发条件的按钮的实现方法。在游戏设计中有一个名词叫“反馈”,大体就是指... 查看详情

就够了(代码片段)

本文目录前言shiro几大核心组件shiro配置信息Cookie被禁用了还可以使用Shiro框架吗?Cookie过期了会自动删除缓存的Session信息吗?shiro实现Cookie、Token双兼容如何做到登出后清除认证、授权、Session缓存?如何对Session进行CRUD测... 查看详情

ajax入门这一篇就够了

什么是AjaxAjax(AsynchronousJavaScriptandXML)异步JavaScript和XMLAjax实际上是下面这几种技术的融合:(1)XHTML和CSS的基于标准的表示技术(2)DOM进行动态显示和交互(3)XML和XSLT进行数据交换和处理(4)XMLHttpRequest进行异步数据检索(5)Javascript将以上... 查看详情

[转]关于深度学习,看这一篇就够了

关于深度学习,看这一篇就够了原文地址:http://www.dlworld.cn/XueXiSuanFa/13.html[日期:2016-04-26]来源:36氪 作者:[字体:大 中 小]    编者按:本文作者王川,投资人,中科大少年班校友,现居加州硅谷,个人微信号... 查看详情

elasticsearch入门,看这一篇就够了(代码片段)

Elasticsearch入门,看这一篇就够了前言可视化工具kibanakibana的安装kibana配置kibana的启动Elasticsearch入门操作操作index创建index索引别名有什么用删除索引查询索引exist索引操作document插入document查询document删除document更新document使用... 查看详情

强化学习入门这一篇就够了!!!万字长文

强化学习强化学习入门这一篇就够了万字长文带你明明白白学习强化学习...强化学习入门这一篇就够了强化学习前言一、概率统计知识回顾1.1随机变量和观测值1.2概率密度函数1.3期望1.4随机抽样二、强化学习的专业术语2.1Stateanda... 查看详情

go全总结学习go这一篇就够了

目录1、基础篇1.1概述1.2变量 1.3常量1.4数据类型 整数类型 查看详情

redis这一篇就够了

概述什么是RedisRedis(RemoteDictionaryServer)是一个使用C语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。Redis可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持... 查看详情

(转)tensorflow深度学习,一篇文章就够了

TensorFlow深度学习,一篇文章就够了2016/09/22· IT技术 · TensorFlow, 深度学习分享到:6 原文出处: 我爱计算机(@tobe迪豪)   作者: 陈迪豪,就职小米科技,深度学习工程师,TensorFlow代... 查看详情

就够了

嗨!大家好,我是小蚂蚁。欢迎关注我的微信公众号【小蚂蚁教你做游戏】,学习更多游戏开发原创教程。最近看有很多问关于游戏背景图的问题,是该选择适应还是选择拉伸?是该适配宽度还是适配高度?不想要背景图去自动... 查看详情

就够了

多年前Android异军突起,成了新的万亿级市场,无数掘金人涌入,期待可以一展拳脚。彼时大环境下的Android行业,只要你能说上来四大组件就能找到工作,走上赛道被浪潮推着前进,行业前景不可谓不光明... 查看详情

超详细的springboot学习教程,springboot学习看这篇就够了

超详细的springBoot学习教程,springboot学习看这篇就够了https://blog.csdn.net/lin1214000999/article/details/105468338/  查看详情