一致性哈希算法小结

在实际生产应用中,经常会设置多台服务器共同组成一个集群对外提供服务,为了确保合理的分配来自客户端的请求,我们会采取负载均衡的策略。例如采用「轮询」的方式让每个节点都能公平的接收到请求;采用「加权轮询」的方式让硬件配置高的节点承担更多的请求。

但是,在分布式系统的环境下,数据有可能是经过「水平切分」后放在不同的节点上的,每个节点存储的数据都是不同的,所以需要采取一种新的策略来保证请求能唯一准确地打到对应的节点上。

哈希算法

最容易想到的方式就是采取「哈希算法」,因为对同一个关键字进行哈希计算,每次计算得到的都是相同的值,这样就可以将某个 key 确定到一个节点上了,可以满足分布式系统的负载均衡需求。

哈希算法最简单的做法就是进行取模运算,假设分布式系统中有 3 个节点,基于 hash(key) % 3 公式对数据进行了映射,如果客户端要获取指定 key 的数据,同样通过上述公式就可以定位到该节点,如果经过公式计算后得到的值是 0,就说明该 key 需要去第一个节点获取。

然而,当服务器节点增加或减少时,原有的映射将失效,大部分数据都需要重新映射到新的服务器节点上,这会导致大规模的数据迁移,影响系统性能和可用性。

哈希冲突的解决办法

哈希冲突是指在使用哈希函数时,不同的输入数据可能会产生相同的哈希值。解决哈希冲突的方法有多种,下面介绍几种常见的方法。

  • 链表法(Separate Chaining):在冲突的位置上创建一个链表,将哈希值相同的键值对链接在一起。当发生冲突时,新的键值对可以被添加到链表中,不会影响其他键值对的位置
  • 开放地址法(Open Addressing):在冲突的位置上探测下一个可用的空槽,直到找到一个空槽或达到最大探测次数。有几种开放地址的方法,如线性探测、二次探测和双重哈希。当发生冲突时,通过探测下一个空槽的方法,将键值对存储在其他位置。开放地址法是一种将冲突的键值对存储在其他位置的方法,而不是像链表法那样使用链表来解决冲突。线性探测是逐个检查下一个槽位,二次探测是使用二次方程进行探测,而双重哈希是使用第二个哈希函数探测。开放地址法的优点是不需要额外的存储空间,但可能会导致聚集(clustering)问题,即冲突的键值对聚集在一起,影响检索性能
  • 再哈希法(Rehashing):当发生冲突时,使用另一个哈希函数计算新的哈希值。如果新的哈希值仍然冲突,可以继续使用其他哈希函数,直到找到不冲突的位置。再哈希法是一种使用不同的哈希函数来解决哈希冲突的方法。优点是可以避免聚集问题,因为使用不同的哈希函数可以分散冲突的键值对。然而,再哈希法需要额外的哈希函数,并且可能需要更多的计算开销
  • 建立更好的哈希函数:改进哈希函数的设计可以减少冲突的概率。例如,使用更复杂的哈希算法,考虑键的所有位,或根据键的特性进行特定设计

选择哪种方法来解决哈希冲突取决于具体的应用场景和需求。不同的方法有不同的优缺点,需要综合考虑性能、存储空间和数据操作的复杂度等因素。

一致性哈希算法

「一致性哈希算法(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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值