写在前面

本文学习自:

https://2382546457.github.io/pages/7e25cb/#_2-%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C

https://www.bilibili.com/video/BV1Hs411j73w/?spm_id_from=333.337.search-card.all.click&vd_source=fa7ba4ae353f08f1d08d1bb24528e96c

简介

一致性Hash是一种特殊的Hash算法,由于其均衡性、持久性的映射特点,被广泛的应用于负载均衡领域,如nginxmemcached都采用了一致性Hash来作为集群负载均衡的方案。

普通哈希

在介绍一致性哈希算法之前,先说一下普通的哈希算法,以便介绍它俩的区别。

在负载均衡中,使用普通的哈希算法进行负载均衡,假如用户上传一个图片,我们有三台服务器可以存储,上传到哪一个图片?

  1. 将服务器编号,0、1、2
  2. 将图片进行哈希算法,hash(图片)= 13,得到的 hash 值再跟机器的数量取模,13 % 3 = 1,将图片存储到服务器1 中。
  3. 以后访问该图片,按照上述顺序得到存储图片的机器即可。

img

但是,普通哈希有个很大的坏处:假如我又添加了两台机器,此时机器数量变成 5,之前那张图片使用 hash(图片)= 13,取模 13 % 5 = 3,于是程序去 服务器2 找图片,但是我们的图片之前存储在 服务器1。bug不就来了?

看到这里你应该意识到:一旦服务器数量修改,hash之后的取模运算就会改变,对应的计算结果也就改变了,之前的计算结果 与 现在的计算结果 不一样,绝大多数数据就需要重新存储。这是个致命错误。

一致性哈希

普通哈希失误就失误在对机器数量取模上,我们不对机器数取模,对 2^32 取模,什么意思呢?

我们首先将 [0, 2^32-1] 围成一个圆圈:

圆圈

如图,0 的左侧就是 2^32-1。我们把这个由 2^32 个数组成的圆圈称为哈希环

于是我们存图片的步骤变为:

将服务器的地址计算哈希值,hash(服务器),用这个值与 2^32 取模,那么所有服务器都会在这个圆圈上代表一个点

image-20231109001357945

将要存储的图片计算哈希值,hash(图片),用这个值与 2^32 取模,那么所有图片都会在这个圆圈上代表一个点

怎么给这些图片选择一个服务器呢?只需要在这个环上一直往后遍历就行了,上图中左边三个图片存储在服务器A,右边的三个图片存储在服务器C。

image-20231109001655962

程序查找时也可以这样做:计算hash值 -> 与 2^32 取模 -> 放在哈希环上往右找第一个服务器。

遇到服务器宕机或者新增服务器时,就可以只破坏一部分数据,保护其余数据。

假如此时 服务器C 宕机,那么右边的三个文件就只能存储到 服务器B 中,但是也仅仅是三个文件。

一致性哈希的的优缺点:

  • 优点 :遇到节点不稳定的情况可以保护多数数据

  • 缺点 :在节点数量太少的情况下,容易因为节点分布不均匀而造成数据倾斜问题,也就是被缓存的数据大都集中存储在某个节点上,从而出现数据分布不均匀的情况,这种情况被称为哈希倾斜

为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个节点都计算多个哈希值,每个计算结果都放置在哈希环上,称为虚拟节点。一个实际的物理节点可以对应多个虚拟节点,虚拟节点越多,哈希环上的节点就越多,数据被均匀分配的概率就越大,哈希环倾斜造成的影响就越小。

寻找过程就变为:hash(图片)-> 取模得到hash值 -> 向右找到虚拟节点 -> 根据虚拟节点找到物理节点

image-20231109003646430