前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一致性 Hash 算法原理&应用梳理

一致性 Hash 算法原理&应用梳理

作者头像
架构精进之路
发布2022-12-21 17:11:31
6050
发布2022-12-21 17:11:31
举报
文章被收录于专栏:架构精进之路架构精进之路

hello,大家好,我是张张,「架构精进之路」公号作者。

前几天在技术群里,看到有小伙伴在讨论一致性hash算法的问题,正好今天以这个为话题,简单介绍下它的原理。下边我们以分布式缓存中经典场景举例,面试中也是经常提及的一些话题,看看什么是一致性hash算法以及它有那些过人之处。

一、背景

考虑这么一种场景:我们有三台缓存服务器编号node0、node1、node2,现在有3000万个key,希望可以将这些个key均匀的缓存到三台机器上,你会想到什么方案呢?

我们可能首先想到的方案是:取模算法hash(key)%N,即:对key进行hash运算后取模,N是机器的数量。

这样,对key进行hash后的结果对3取模,得到的结果一定是0、1或者2,正好对应服务器node0、node1、node2,存取数据直接找对应的服务器即可,简单粗暴,完全可以解决上述的问题。

取模算法虽然使用简单,但对机器数量取模,在集群扩容和收缩时却有一定的局限性:因为在生产环境中根据业务量的大小,调整服务器数量是常有的事,而服务器数量N发生变化后hash(key)%N计算的结果也会随之变化

比如:一个服务器节点挂了,计算公式从hash(key)% 3变成了hash(key)% 2,结果会发生变化,此时想要访问一个key,这个key的缓存位置大概率会发生改变,那么之前缓存key的数据也会失去作用与意义。

大量缓存在同一时间失效,造成缓存的雪崩,进而导致整个缓存系统的不可用,这基本上是不能接受的。为了解决优化上述情况,一致性hash算法应运而生~

二、一致性Hash算法原理

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系;

一致性哈希解决了简单哈希算法在分布式哈希表(Distributed Hash Table,DHT)中存在的动态伸缩等问题。

一致性hash算法本质上也是一种取模算法。不过,不同于上边按服务器数量取模,一致性hash是对固定值2^32取模。

IPv4的地址是4组8位2进制数组成,所以用2^32可以保证每个IP地址会有唯一的映射。

  • hash环

我们可以将这2^32个值抽象成一个圆环⭕️,圆环的正上方的点代表0,顺时针排列,以此类推:1、2、3…直到2^32-1,而这个由2的32次方个点组成的圆环统称为hash环。

  • 服务器映射到hash环

在对服务器进行映射时,使用hash(服务器ip)% 2^32,即:使用服务器IP地址进行hash计算,用哈希后的结果对2^32取模,结果一定是一个0到2^32-1之间的整数。

而这个整数映射在hash环上的位置代表了一个服务器,依次将node0、node1、node2三个缓存服务器映射到hash环上。

  • 对象key映射到服务器

在对对应的Key映射到具体的服务器时,需要首先计算Key的Hash值:hash(key)% 2^32。

:此处的Hash函数可以和之前计算服务器映射至Hash环的函数不同,只要保证取值范围和Hash环的范围相同即可(即:2^32)

将Key映射至服务器遵循下面的逻辑:

从缓存对象key的位置开始,沿顺时针方向遇到的第一个服务器,便是当前对象将要缓存到的服务器

假设我们有“semlinker”、“kakuqo”、“lolo”、“fer”四个对象,分别简写为o1、o2、o3和o4。

首先,使用哈希函数计算这个对象的hash值,值的范围是 [0,2^32-1]:

图中对象的映射关系如下:

代码语言:javascript
复制
hash(o1) = k1; hash(o2) = k2;
hash(o3) = k3; hash(o4) = k4;

同时3台缓存服务器,分别为CS1、CS2和CS3:

则可知,各对象和服务器的映射关系如下:

代码语言:javascript
复制
K1 => CS1
K4 => CS3
K2 => CS2
K3 => CS1

即:

以上便是一致性Hash的工作原理。

可以看到,一致性Hash就是:将原本单个点的Hash映射,转变为了在一个环上的某个片段上的映射

三、服务器扩缩容应用场景

下面我们来看几种服务器扩缩容的场景:

  • 服务器减少

假设CS3服务器出现故障导致服务下线,这时原本存储于CS3服务器的对象o4,需要被重新分配至CS2服务器,其它对象仍存储在原有的机器上:

此时受影响的数据只有CS2和CS3服务器之间的部分数据!

  • 服务器增加

假如业务量激增,我们需要增加一台服务器CS4,经过同样的hash运算,该服务器最终落于t1和t2服务器之间,具体如下图所示:

此时,只有t1和t2服务器之间的部分对象需要重新分配。

在以上示例中只有o3对象需要重新分配,即它被重新到CS4服务器。

在前面我们已经说过:如果使用简单的取模方法,当新添加服务器时可能会导致大部分缓存失效,而使用一致性哈希算法后,这种情况得到了较大的改善,因为只有少部分对象需要重新分配!

四、数据偏斜与平衡问题

  • 引出问题

在上面给出的例子中,各个服务器几乎是平均被均摊到Hash环上。

但是在实际场景中很难选取到一个Hash函数这么完美的将各个服务器散列到Hash环上。

此时,在服务器节点数量太少的情况下,很容易因为节点分布不均匀而造成数据倾斜问题。

如下图被缓存的对象大部分缓存在node-4服务器上,导致其他节点资源浪费,系统压力大部分集中在node-4节点上,这样的集群是非常不健康的:

同时,还有另一个问题:

在上面新增服务器CS4时,CS4只分担了CS1服务器的负载,服务器CS2和CS3并没有因为CS4服务器的加入而减少负载压力;如果CS4服务器的性能与原有服务器的性能一致甚至可能更高,那么这种结果并不是我们所期望的。

  • 虚拟节点

针对上面的问题,我们可以通过:引入虚拟节点来解决负载不均衡的问题:即将每台物理服务器虚拟为一组虚拟服务器,将虚拟服务器放置到哈希环上,如果要确定对象的服务器,需先确定对象的虚拟服务器,再由虚拟服务器确定物理服务器

如下图所示:

在图中:o1和o2表示对象,v1~v6表示虚拟服务器,s1~s3表示实际的物理服务器。

  • 虚拟节点的计算

虚拟节点的hash计算通常可以采用:对应节点的IP地址加数字编号后缀 hash(10.24.23.227#1) 的方式;

举个例子,node-1节点IP为10.24.23.227,正常计算node-1的hash值:

  • hash(10.24.23.227#1)% 2^32

假设我们给node-1设置三个虚拟节点,node-1#1、node-1#2、node-1#3,对它们进行hash后取模:

  • hash(10.24.23.227#1)% 2^32
  • hash(10.24.23.227#2)% 2^32
  • hash(10.24.23.227#3)% 2^32

注意:

  • 分配的虚拟节点个数越多,映射在hash环上才会越趋于均匀,节点太少的话很难看出效果。
  • 引入虚拟节点的同时也增加了新的问题,要做虚拟节点和真实节点间的映射,对象key->虚拟节点->实际节点之间的转换。

五、一致性Hash算法使用场景

一致性hash在分布式系统中应该是实现负载均衡的首选算法,它的实现比较灵活,既可以在客户端实现,也可以在中间件上实现,比如日常使用较多的缓存中间件memcached和redis集群都有用到它。

memcached的集群比较特殊,严格来说它只能算是伪集群,因为它的服务器之间不能通信,请求的分发路由完全靠客户端来的计算出缓存对象应该落在哪个服务器上,而它的路由算法用的就是一致性hash。

还有redis集群中hash槽的概念,虽然实现不尽相同,但思想万变不离其宗,看完本篇的一致性hash,你再去理解redis槽位就轻松多了。

其它的应用场景还有很多:

  • RPC框架Dubbo用来选择服务提供者
  • 分布式关系数据库分库分表:数据与节点的映射关系
  • LVS负载均衡调度器
  • ……

六、总结

本文抛砖引玉的讲解了一致性Hash算法的原理及应用场景介绍。

Hash算法作为一种活动开发经常遇到的算法,我们在使用中不仅仅要知道这种算法背后真正的原理,才可以在使用上做到有的放矢。Hash的相关知识还有很多,有兴趣的同学可以继续深入研究。

大家在实际使用时,可以根据需要,搭配实际的组件!


希望今天的讲解对大家有所帮助,谢谢!

Thanks for reading!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-09-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构精进之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • hello,大家好,我是张张,「架构精进之路」公号作者。
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档