一致性哈希算法小结

news/2024/11/8 7:30:12/

在实际生产应用中,经常会设置多台服务器共同组成一个集成对外提供服务,为了确保合理的分配来自客户端的请求,我们会采取负载均衡的策略。例如采用「轮询」的方式让每个节点都能公平的接收到请求;采用「加权轮询」的方式让硬件配置高的节点承担更多的请求。
但是,在分布式系统的环境下,数据有可能是经过「水平切分」后放在不同的节点上的,每个节点存储的数据都是不同的,所以需要采取一种新的策略来保证请求能唯一准确地打到对应的节点上。

哈希算法

最容易想到的方式就是采取「哈希算法」,因为对同一个关键字进行哈希计算,每次计算得到的都是相同的值,这样就可以将某个 key 确定到一个节点上了,可以满足分布式系统的负载均衡需求。
哈希算法最简单的做法就是进行取模运算,假设分布式系统中有 3 个节点,基于hash(key) % 3公式对数据进行了映射,如果客户端要获取指定 key 的数据,同样通过上述公式就可以定位到该节点,如果经过公式计算后得到的值是 0,就说明该 key 需要去第一个节点获取。
然而,当服务器节点增加或减少时,原有的映射将失效,大部分数据都需要重新映射到新的服务器节点上,这会导致大规模的数据迁移,影响系统性能和可用性。

一致性哈希算法

「一致性哈希算法(Consistent Hashing)」是一种用于分布式系统中数据分片和负载均衡的哈希算法。它解决了传统哈希算法带来的服务器节点增减时数据重新分布的问题,同时在保持负载均衡的情况下,尽量少地迁移数据。
一致性哈希算法也采用了取模运算,但与哈希算法不同的是,哈希算法是针对节点的数量进行取模运算,而一致性哈希算法是对2^32进行取模运算,是一个固定的值。
我们可以把一致性哈希算法对2^32进行取模运算的结果值组织成一个圆环,就像钟表一样,通过将哈希空间设计为一个环形结构,这个圆环也被称为哈希环。

在这里插入图片描述

为什么要选择对2^32进行取模运算呢?

  • 哈希环的均匀性:2^32是一个很大的数值,取模运算可以将哈希值分散在整个哈希环上,使得数据在哈希环上的分布更均匀。这样可以保持节点负载均衡,减少数据倾斜的可能性;
  • 范围的大小:2^32等于 4294967296,这个范围足够大,可以容纳绝大多数哈希值。即使在非常大规模的系统中,仍然可以通过取模运算对节点进行充分的分布;
  • 效率的考虑:取模运算是一种简单高效的运算,可以在常数时间内完成,不会造成太大的计算开销;
  • 可扩展性的考虑:当需要增加或删除服务器节点时,可以方便地调整哈希环的大小(例如 2^32)来适应节点数量的变化,而不会影响已有数据的映射关系

总之,选择对2^32进行取模运算是为了保证哈希环的均匀性、范围的大小、计算效率以及可扩展性。这样可以提高一致性哈希算法的分布均衡性和系统的性能。
一致性哈希算法要进行两步哈希:

  1. 对存储节点进行哈希计算,即对存储节点做哈希映射,比如根据节点的 IP 地址进行哈希;
  2. 当需要对数据进行存储或访问时,对数据进行哈希映射

所以,一致性哈希是指将「存储节点」和「数据」都映射到一个首尾相连的哈希环上。
那么,对「数据」进行哈希映射得到一个结果,要怎样找到存储该数据的节点呢?答案是:映射的结果值往顺时针的方向找到的第一个节点,就是存储该数据的节点。

在这里插入图片描述

如上图所示,有 3 个节点经过哈希计算,映射到了哈希环上不同的位置。接着,对要查询的 key-1 进行哈希计算,确定 key-1 映射在哈希环上的位置后,从这个位置往顺时针的方向找到的第一个节点,就是存储该 key-1 数据的节点。
由于哈希环是一个闭合的环形结构,增加或删除一个服务器节点,只会影响到环上和该节点相邻的部分,而不会影响整个环。因此,当服务器节点发生变化时,只需要重新映射和迁移少量的数据即可,而不会对整个系统产生重大影响。

在这里插入图片描述

在这里插入图片描述

引入虚拟节点

但是一致性哈希算法并不能保证节点都能够在哈希环上分布均匀,这样就会带来一个问题,会有大量的请求都集中在一个节点上。在这种节点分布不均匀的情况下,进行容灾与扩容时,哈希环上的相邻节点容易受到过大影响,发生雪崩式的连锁反应。
比如,下图中如果节点 A 被移除了,当节点 A 宕机后,根据一致性哈希算法的规则,其上的数据应该全部迁移到相邻的节点 B 上,这样节点 B 的数据量、访问量都会迅速增加,而一旦新增的压力超过了节点 B 的处理能力上限,就会导致节点 B 的崩溃,进而形成雪崩式的连锁反应。

在这里插入图片描述

所以,一致性哈希算法虽然减少了数据迁移量,但是会存在节点分布不均匀的问题,这时我们可以引入虚拟节点的概念。
要想解决节点在哈希环上分布不均匀的问题,就是需要有大量的节点,节点数越多,哈希环上的节点分布地就越均匀。具体做法是:不再将真实节点映射到哈希环上,而是将虚拟节点映射到哈希环上,并将虚拟节点映射到真实节点,即这里有两层映射关系。

在这里插入图片描述

可以看到,随着节点数量的增多,节点在哈希环上的分布就相对均匀了。这时,如果有请求寻址到 A-01 这个虚拟节点,接着再通过 A-01 虚拟节点找到其真实节点 A,这样请求就能打到真实节点 A 上了。
上图中每个真实节点仅包含 3 个虚拟节点,实际上这样能起到的均衡效果很有限。在实际的工程中,虚拟节点的数量会有很多,比如 Nginx 的一致性哈希算法,每个权重为 1 的真实节点就包含了 160 个虚拟节点。
另外,虚拟节点除了会提高节点的均衡度之外,还会提高系统的稳定性。当节点发生变化时,会有不同的节点来共同分担系统的变化,因此稳定性会更高。比如,当某个节点被移除时,对应的该节点的多个虚拟节点也会被移除,而这些虚拟节点按顺时针方向找到的下一个虚拟节点,可能会对应着不同的真实节点,即这些不同的真实节点可以共同分担因节点变化而产生的压力。而且,有了虚拟节点之后,还可以为硬件配置更好的节点增加权重,比如对权重更高的节点增加更多的虚拟节点。
因此,引入虚拟节点的一致性哈希算法不仅适合于硬件配置不同的节点的场景,而且适合于节点规模会发生变化的场景。

参考资料

  • https://xiaolincoding.com/os/8_network_system/hash.html#_9-4-%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C

http://www.ppmy.cn/news/814084.html

相关文章

硬盘初始化分区选择GPT还是MBR?

新硬盘初始化时,选择分区表类型由硬盘的容量决定。 对于总容量小于或等于2TB的硬盘,分区表可以选择MBR,也可以选择GPT。从兼容性考虑的话,一般建议使用MBR分区表就可以满足使用要求了。 对于总容量大于2TB的硬盘,必须…

linux需要GPT初始化磁盘吗,PVE里面使用GPT初始化磁盘选项为灰色的解决办法

一台J1900小主机,安装了PVE虚拟化系统玩,很爽,但是随之问题来了。 由于MSATA空间不够,使用一个USB3.0的硬盘盒插了个SSD来扩展存储空间。但是发现USB不方便,不如直接把SSD接在板载的SATA接口上更好,然后关机,在PVE界面删除了之前建立的pve分区。 但是当把SSD接在SATA接口…

腾讯云服务器挂载云硬盘数据盘并初始化云硬盘

在腾讯云后台挂载云硬盘 进入CVM或者Lighthouse管理,这里以Lighthouse为例。选中数据盘,选择实例并挂载,大约需要1分钟。 手动挂载云硬盘后,云硬盘为脱机状态,需登录实例完成初始化操作使云硬盘可用。 在Linux系统中…

ubuntu 初始化分区

一般新买的硬盘需要初始化之后才能使用,因为上面没有文件系统,如果你买的硬盘直接就能挂载和使用,那么它有可能是。。。老司机。 直接挂载新的硬盘会出现这样的报错: mount: /mnt: wrong fs type, bad option, bad superblock o…

指定计算机上的虚拟硬盘,初始化新加的虚拟硬盘

初始化新加的虚拟硬盘 (2015-01-06 10:04:58) 标签: parallels 初始化新虚拟硬盘 在Parallels Desktop中,将新的空白虚拟硬盘添加到虚拟机配置后,对于安装在虚拟机中的操作系统来说它将不可见,直至将其初始化。 初始化 Windows 中的新虚拟硬盘 要初始化Windows虚拟机操作系…

如何将元素排成一行并水平居中?

1、创建一个容器元素:首先,创建一个容器元素来包含要排列的元素。可以使用一个div元素或其他适当的容器元素。 2、设置容器元素为Flex容器:将容器元素的CSS属性display设置为flex,将其定义为Flex容器。例如: .contai…

大笔和小笔

最小的单位是bit,之后网络兴起后,才有了byte,就是字节byte:8个二进制位为一个字节(B),最常用的单位。往后基于信息技术的发展,基于byte发展起来各种流量单位,如下所示: 1 Byte&…

Ps--钢笔工具(1)

钢笔工具是Ps中比较常用且重要的工具。 钢笔工具是在绘图软件中,用来创造路径的工具,创造路径后,还可再编辑。钢笔工具属于矢量绘图工具,其优点是可以勾画平滑的曲线,在缩放或者变形之后仍能保持平滑效果。 鼠标右点左…