面试官:怎么改进哈希算法实现负载均衡的扩展性和容错性?

什么是哈希算法

数据结构中我们学习过哈希表也称为散列表,我们来回顾下散列表的定义。

散列表,是根据键直接访问在指定储存位置数据的数据结构。通过计算一个关于键的函数也称为哈希函数,将所需查询的数据映射到表中一个位置来访问记录,加快查找速度。这个映射函数称做「散列函数」,存放记录的数组称做散列表。

散列函数能使对一个数据序列的访问过程更加迅速有效,是一种空间换时间的算法,通过散列函数数据元素将被更快定位。

下图示意了字符串经过哈希函数映射到哈希表的过程。没错,输入字符串是用脸滚键盘打出来的:)

哈希示意图.png

常见的哈希算法有MD5、CRC 、MurmurHash 等算法,简单介绍一下。

MD5算法

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),MD5算法将数据(如一段文字)运算变为另一固定长度值,是散列算法的基础原理。由美国密码学家 Ronald Linn Rivest设计,于1992年公开并在 RFC 1321 中被加以规范。

CRC算法

循环冗余校验(Cyclic Redundancy Check)是一种根据网络数据包或电脑文件等数据,产生简短固定位数校验码的一种散列函数,由 W. Wesley Peterson 于1961年发表。生成的数字在传输或者存储之前计算出来并且附加到数据后面,然后接收方进行检验确定数据是否发生变化。由于本函数易于用二进制的电脑硬件使用、容易进行数学分析并且尤其善于检测传输通道干扰引起的错误,因此获得广泛应用。

MurmurHash算法

MurmurHash 是一种非加密型哈希函数,适用于一般的哈希检索操作。由 Austin Appleby 在2008年发明,并出现了多个变种,与其它流行的哈希函数相比,对于规律性较强的键,MurmurHash的随机分布特征表现更良好。

这个算法已经被很多开源项目使用,比如libstdc++ (4.6版)、Perl、nginx (不早于1.0.1版)、Rubinius、 libmemcached、maatkit、Hadoop等。

常见散列方法

  • 直接定址法:取关键字或关键字的某个线性函数值为散列地址,这个线性函数的定义多种多样,没有标准。

  • 数字分析法:假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。

  • 平方取中法:取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的,取的位数由表长决定。

  • 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。

  • 取模法:取关键字被某个不大于散列表表长 m 的数 p 除后所得的余数为散列地址。即 hash(key) = key % p(p<= M),不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后取模。对 p 的选择很重要,一般取素数或 m,若 p 选择不好,容易产生冲突。

缓存系统负载均衡

在分布式集群缓存的负载均衡实现中,比如 memcached 缓存集群,需要把缓存数据的 key 利用哈希函数散列,这样缓存数据能够均匀分布到各个分布式存储节点上,要实现这样的负载均衡一般可以用哈希算法来实现。下图演示了这一分布式存储过程:

分布式缓存散列存储示意图

普通哈希算法负载均衡

前面我们介绍过各种散列方法,不管是选择上述哪种散列方法,在这个应用场景下,都是要把缓存数据利用哈希函数均匀的映射到服务器集群上,我们就选择简单的「取模法」来说明这个过程。

假设有 3 个服务器节点编号 [0 - 2],6 个缓存键值对编号 [1 - 6],则完成哈希映射之后,三个缓存数据映射情况如下:

哈希计算公式:key % 节点总数 = Hash节点下标
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0

缓存哈希实例

每个连接都均匀的分散到了三个不同的服务器节点上,看起来很完美!

但是,在分布式集群系统的负载均衡实现上,这种模型有两个问题:

1. 扩展能力差

为了动态调节服务能力,服务节点经常需要扩容缩容。打个比方,如果是电商服务,双十一期间的服务机器数量肯定要比平常大很多,新加进来的机器会使原来计算的哈希值不准确,为了达到负载均衡的效果,要重新计算并更新哈希值,对于更新后哈希值不一致的缓存数据,要迁移到更新后的节点上去。

假设新增了 1 个服务器节点,由原来的 3 个服务节点变成 4 个节点编号 [0 - 3],哈希映射情况如下:

哈希计算公式:key % 节点总数 = Hash节点下标
1 % 4 = 1
2 % 4 = 2
3 % 4 = 3
4 % 4 = 0
5 % 4 = 1
6 % 4 = 2

可以看到后面三个缓存 key :4、5、6 对应的存储节点全部失效了,这就需要把这几个节点的缓存数据迁移到更新后的节点上 (费时费力) ,也就是由原来的节点 [1, 2, 0] 迁移到节点 [0, 1, 2],迁移后存储示意图如下:

2. 容错能力不佳

线上环境服务节点虽然有各种高可用性保证,但还是是有宕机的可能,即使没有宕机也有缩容的需求。不管是宕机和缩容都可以归结为服务节点删除的情况,下面分析下服务节点删除对负载均衡哈希值的影响。

假设删除 1 个服务器节点,由最初的 3 个服务节点变成 2 个,节点编号 [0 - 1],哈希映射情况如下:

哈希计算公式:key % 节点总数 = Hash节点下标
1 % 2 = 1
2 % 2 = 0
3 % 2 = 1
4 % 2 = 0
5 % 2 = 1
6 % 2 = 0

下图展示普通哈希负载均衡算法在一个节点宕机时候,导致的的缓存数据迁移分布情况:

如图所见,在这个例子中,仅仅删除了一个服务节点,也导致了哈希值的大面积更新,哈希值的更新也是意味着节点缓存数据的迁移(缓存数据表示心好累)。

一致性哈希算法负载均衡

正是由于普通哈希算法实现的缓存负载均衡存在扩展能力和容错能力差问题,所以我们引入一致性哈希算法,那么什么是一致性哈希呢?先来看下wiki上对一致性Hash的定义

一致哈希由 MIT 的 David Karger 及其合作者提出,现在这一思想已经扩展到其它领域。在这篇1997年发表的学术论文中介绍了一致哈希如何应用于用户易变的分布式Web服务中。一致哈希也可用于实现健壮缓存来减少大型Web应用中系统部分失效带来的负面影响。

这篇描述一致性哈希的论文发表于1997年,阅读无障碍的同学可以直接看看大佬的论文理解更深刻,附上论文下载链接:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.147.1879

一致性hash论文

一句话概括一致性哈希:就是普通取模哈希算法的改良版,哈希函数计算方法不变,只不过是通过构建环状的 Hash 空间代替普通的线性 Hash 空间。具体做法如下:

首先,选择一个足够大的Hash空间(一般是 0 ~ 2^32)构成一个哈希环。

一致性哈希环

然后,对于缓存集群内的每个存储服务器节点计算 Hash 值,可以用服务器的 IP 或 主机名计算得到哈希值,计算得到的哈希值就是服务节点在 Hash 环上的位置。

节点哈希

最后,对每个需要存储的数据 key 同样也计算一次哈希值,计算之后的哈希也映射到环上,数据存储的位置是沿顺时针的方向找到的环上的第一个节点。下图举例展示了节点存储的数据情况,我们下面的说明也是基于目前的存储情况来展开。

原理讲完了,来看看为什么这样的设计能解决上面普通哈希的两个问题

扩展能力提升

前面我们分析过,普通哈希算法当需要扩容增加服务节点的时候,会导致原油哈希映射大面积失效。现在,我们来看下一致性哈希是如何解决这个问题的。

如下图所示,当缓存服务集群要新增一个节点node3时,受影响的只有 key3 对应的数据 value3,此时只需把 value3 由原来的节点 node0 迁移到新增节点 node3 即可,其余节点存储的数据保持不动

一致性哈希-扩展节点

容错能力提升

普通哈希算法当某一服务节点宕机下线,也会导致原来哈希映射的大面积失效,失效的映射触发数据迁移影响缓存服务性能,容错能力不足。一起来看下一致性哈希是如何提升容错能力的。

如下图所示,假设 node2 节点宕机下线,则原来存储于 node2 的数据 value2 和 value5 ,只需按顺时针方向选择新的存储节点 node0 存放即可,不会对其他节点数据产生影响。一致性哈希能把节点宕机造成的影响控制在顺时针相邻节点之间,避免对整个集群造成影响

一致性哈希-删除节点

一致性哈希优化

存在的问题

上面展示了一致性哈希如何解决普通哈希的扩展和容错问题,原理比较简单,在理想情况下可以良好运行,但在实际使用中还有一些实际问题需要考虑,下面具体分析。

数据倾斜

试想一下若缓存集群内的服务节点比较少,就像我们例子中的三个节点,而哈希环的空间又有很大(一般是 0 ~ 2^32),这会导致什么问题呢?

可能的一种情况是,较少的服务节点哈希值聚集在一起,比如下图所示这种情况 node0 、node1、node2 聚集在一起,缓存数据的 key 哈希都映射到 node2 的顺时针方向,数据按顺时针寻找存储节点就导致全都存储到 node0 上去,给单个节点很大的压力!这种情况称为数据倾斜

一致性哈希-数据倾斜

节点雪崩

数据倾斜和节点宕机都可能会导致缓存雪崩。

雪崩

拿前面数据倾斜的示例来说,数据倾斜导致所有缓存数据都打到 node0 上面,有可能会导致 node0 不堪重负被压垮了,node0 宕机,数据又都打到 node1 上面把 node1 也打垮了,node1 也被打趴传递给 node2,这时候故障就像像雪崩时滚雪球一样越滚越大

还有一种情况是节点由于各种原因宕机下线。比如下图所示的节点 node2 下线导致原本在node2 的数据压到 node0 , 在数据量特别大的情况下也可能导致节点雪崩,具体过程就像刚才的分析一样。

总之,连锁反应导致的整个缓存集群不可用,就称为节点雪崩

一致性哈希-节点雪崩

虚拟节点

那该如何解决上述两个棘手的问题呢?可以通过「虚拟节点」的方式解决。

所谓虚拟节点,就是对原来单一的物理节点在哈希环上虚拟出几个它的分身节点,这些分身节点称为「虚拟节点」。打到分身节点上的数据实际上也是映射到分身对应的物理节点上,这样一个物理节点可以通过虚拟节点的方式均匀分散在哈希环的各个部分,解决了数据倾斜问题

由于虚拟节点分散在哈希环各个部分,当某个节点宕机下线,他所存储的数据会被均匀分配给其他各个节点,避免对单一节点突发压力导致的节点雪崩问题。

下图展示了虚拟节点的哈希环分布,其中左边是没做虚拟节点情况下的节点分布,右边背景色绿色两个的 node0 节点是 node0 节点的虚拟节点;背景色红色的 node1 节点是 node1 的虚拟节点。

一致性哈希-虚拟节点

总结

本文首先介绍了什么是哈希算法和常见的哈希算法,以及常见散列方式,接着说明基于普通哈希算法的缓存负载均衡实现,并举例说明普通算法的扩展性和容错性方便存在的问题。

为了解决普通算法的扩展性和容错性问题引入一致性哈希算法,图解和举例分析了一致性哈希是如何提高扩展性和容错性。最后粗糙的一致性哈希算法也存在数据倾斜和节点雪崩的问题,讲解了如何利用虚拟节点优化一致性哈希算法,解决数据倾斜和雪崩问题。至此,一致性哈希你学会了吗?

一致性哈希这个知识点不难,但是经常会考察到,就像布隆过滤器算法一样,没听过的人觉得很高端,研究一下也就那么一回事,所以知识面要宽才能吊打面试官啊同学们!

感谢各位的阅读,文章的目的是分享对知识的理解,技术类文章我都会反复求证以求最大程度保证准确性,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。

面试官:怎么改进哈希算法实现负载均衡的扩展性和容错性?我:...相关推荐

  1. ribbon 默认负载均衡 是什么_面试官:说说Ribbon是如何实现负载均衡的?

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 来源:blog.csdn.net/LO_YUN/article/details/ ...

  2. 面试官:你能说说Ribbon的负载均衡策略及原理嘛?

    来源:blog.csdn.net/wudiyong22/article/details/80829808 Load Balance负载均衡是用于解决一台机器(一个进程)无法解决所有请求而产生的一种算法 ...

  3. 面试时遇到一致性哈希算法这样回答会让面试官眼前一亮

    [CSDN 编者按]很多人都知道什么是哈希函数,在后端面试和开发中会遇到"一致性哈希",那什么是一致性哈希呢,当面试官问到你又该如何给出漂亮的回答. 作者 | 丁威       责 ...

  4. 【BAT面试必备】一致性哈希算法原理 一文吊打面试官

    一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致 ...

  5. 7年工作经验,面试官竟然让我写算法题?

    有一位阿里工程师在脉脉上吐槽:自己明明有着BAT的5年工作经验,却没想到在应聘Hulu时,败在了算法题上. 明明有多年大厂工作经验,为什么还要在找工作跳槽时,面对算法的考题?    01  为啥美国的 ...

  6. 面试官问出这几道算法题,你能扛住么?

    写在前面 CSDN话题挑战赛第1期 活动详情地址:https://marketing.csdn.net/p/bb5081d88a77db8d6ef45bb7b6ef3d7f 参赛话题:前端面试宝典 话 ...

  7. 6 年大厂面试官,谈谈我对算法岗面试的一些看法

    文 | 不敢透露姓名的 Severus 和小轶 面试官坐在那撇着大嘴的,"咳,给你一机会,最短的时间内让我记住你."这个我会,我抡圆了"啪!",扭头我就走.我刚 ...

  8. Nginx面试三连问:Nginx如何工作?负载均衡策略有哪些?如何限流?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 1.什么是Nginx,谈谈个人都理解,项目中是否用到,为什 ...

  9. java轮训算法_负载均衡轮询算法实现疑问

    import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /* ...

最新文章

  1. 如何获取boss直聘搜索牛人被屏蔽的姓名
  2. torch Dataset类
  3. 数学系列 - 概率论 - 泊松分布和(负)指数分布
  4. 2019ICPC(南京) - super_log(欧拉降幂)
  5. 【工作经验分享】不会真有人觉得mybatis很难学吧
  6. 驱动开发 环境搭建(VS2008+WDK+DDKWzard)
  7. python学习之random
  8. 大数据之-Hadoop之HDFS_基于JAVA的开发_客户端环境测试---大数据之hadoop工作笔记0055
  9. 【ZZ】国外大型网站使用到编程语言 | 菜鸟教程
  10. C++笔记(1):使用STL中sort()对struct排序
  11. visual studio 2012 下配置OPENcv3.1 和CMAKE问题总结
  12. c#等待所有子线程执行完毕方法
  13. 我们终将死去,这难道不够美好吗?
  14. ie11 html5播放器卡,GitHub - yangyin/html5-player: 基于react的h5播放器
  15. CentOs 7源码安装 Python3
  16. 34万奖金!第三届厦门国际银行数创金融杯金融营销大赛来啦!
  17. 基于AI+RT-THREAD的人检测入侵检测摄像头(一 AI模型的导入及处理)
  18. eclipse配置python解析器_Eclipse中配置Python环境
  19. 群晖 NAS 通过 frp 实现内网穿透
  20. 模拟赛20200228(yyq)【右链+dfs序,子树管辖,聚集水流问题】

热门文章

  1. SpringBoot FK-关联表查询(二)
  2. pureftpd 如何修改管理员密码
  3. GetWindowThreadProcessId
  4. CodeSmith将模板文件批量生成文件的方法
  5. 在Linux中模拟击键和鼠标移动
  6. 过http user-agent判断是否为手机浏览器
  7. Linux: xclip,pbcopy,xsel用法 terminal 复制粘帖 (mac , ubuntu)
  8. UIKeyboard键盘相关知识点
  9. 点击空链接,页面不跳到页头
  10. input 提交表单按钮 背景图片的完美设置 (隐藏字体)