Redis 分槽机制与一致性哈希详解

637 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

Redis 分槽机制是其实现集群部署的方案。一个集群共有 16384 个哈希槽,Redis Cluster会自动把这些槽平均分布在集群实例上。Redis Cluster 会对 key 进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。

1. 节点迁移

对于 Redis 而言,不管是扩容还是缩容的节点迁移过程都大同小异:

image.png

  • 通知目标节点target发送准备导入槽slot数据。

  • 通知源节点source准备迁出槽数据。

  • 源节点source获取count个数据槽slot的键。

  • 源节点进行指定key迁移。

  • 重复上面两步,直到槽slot下所有的键值数据迁移到目标节点target。

  • 通知集群,槽slot已经分配给目标节点target。

2. MOVED 与 ASK

当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个 MOVED 指令并携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。

客户端收到 MOVED 指令后,要立即纠正本地的槽位映射表。后续所有 key 将使用新的槽位映射表。

集群的伸缩(扩容/缩容)的时候,当我们去源节点访问时,发现key已经迁移到目标节点,会回复ASK转向异常,收到异常后,先是执行ASK命令,然后给目标节点再次发送命令,然后就会返回结果。

客户端保存了槽位和节点的映射关系表,当客户端收到MOVED指令的时候,他会去刷新槽位映射关系表,获取到最新的映射关系。当收到ASK转向异常时,不会刷新槽位映射关系表,因为它是临时纠正。

3. 一致性哈希

3.1 传统哈希取余

Redis 分槽机制就是实现一致性哈希的方式之一,而一致性哈希说白了,就是复杂均衡算法的一种。

传统的哈希负载均衡算法如下所示:

image.png

直接对于请求的key值进行哈希取余,来决定其需要分配到哪台服务器上。

3.2 一致性哈希

但是,如果采取传统的哈希取余,当我们需要进行扩缩容时,就会面临对所有的请求的哈希迁移问题,开销是很大的,因此,就引出了我们的一致性哈希。

一致哈希算法也用了取模运算,但与哈希算法不同的是,哈希算法是对节点的数量进行取模运算,而一致哈希算法是对 2^32 进行取模运算,是一个固定的值

我们可以把一致哈希算法是对 2^32 进行取模运算的结果值组织成一个圆环,就像钟表一样,钟表的圆可以理解成由 60 个点组成的圆,而此处我们把这个圆想象成由 2^32 个点组成的圆,这个圆环被称为哈希环,如下图:

image.png

当需要对指定 key 的值进行读写的时候,要通过下面 2 步进行寻址:

  • 首先,对 key 进行哈希计算,确定此 key 在环上的位置;
  • 然后,从这个位置沿着顺时针方向走,遇到的第一节点就是存储 key 的节点。

假设节点数量从 3 增加到了 4,新的节点 D 经过哈希计算后映射到了下图中的位置:

image.png

在一致哈希算法中,如果增加或者移除一个节点,仅影响该节点在哈希环上顺时针相邻的后继节点,其它数据也不会受到影响

3.3 哈希偏移以及虚拟节点

一致性哈希算法虽然减少了数据迁移量,但是存在节点分布不均匀的哈希偏移问题(比如说上图节点B分配的key远少于节点C分配的key)。

为了解决这个问题,就引入了虚拟节点的概念,具体做法是,不再将真实节点映射到哈希环上,而是将虚拟节点映射到哈希环上,并将虚拟节点映射到实际节点

image.png

虚拟节点除了会提高节点的均衡度,还会提高系统的稳定性。当节点变化时,会有不同的节点共同分担系统的变化,因此稳定性更高