🗒️切片集群
2024-9-20
| 2024-9-20
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password

纵向扩展和横向扩展对比

  • 纵向扩展
    • 定义:升级单个 Redis 实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的 CPU。
    • 优点:实施起来简单、直接。
    • 缺点(存在的问题)
      • 当使用 RDB 对数据进行持久化时,如果数据量增加,需要的内存也会增加,主线程 fork 子进程时就可能会阻塞。原因是 fork 操作的用时和 Redis 的数据量是正相关的,而 fork 在执行时会阻塞主线程。数据量越大,fork 操作造成的主线程阻塞的时间越长。
      • 纵向扩展会受到硬件和成本的限制。单机硬件不能无限增加,而且边际效应明显。
  • 横向扩展
    • 定义:横向增加当前 Redis 实例的个数,就像下图中,原来使用 1 个 8GB 内存、50GB 磁盘的实例,现在使用三个相同配置的实例。
    • 优点:只用增加 Redis 的实例个数就行了,不用担心单个实例的硬件和成本限制。
    • 需要解决的问题
      • 数据切片后,在多个实例之间如何分布?
      • 客户端怎么确定想要访问的数据在哪个实例上?

数据切片和实例的对应分布关系

切片集群和 Redis Cluster 的联系与区别

  • 切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。
  • 从 3.0 开始,官方提供了一个名为 Redis Cluster 的方案,用于实现切片集群。
Redis Cluster 方案采用哈希槽(Hash Slot,接下来我会直接称之为 Slot),来处理数据和实例之间的映射关系
在 Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中。哈希槽又会被映射到具体的 Redis 实例上(准确来说,Redis 实例拥有哪些哈希槽)。
💡
哈希槽相当于抽象层,将 key 的分布与 Redis 实例解耦,便于 Redis 的负载均衡。

映射的步骤(key 到 Hash Slot)

哈希槽的映射过程主要有两步:
  1. 首先根据键值对的 key,按照 CRC162 算法计算一个 16 bit 的值;
  1. 然后,再用这个 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽。

哈希槽的分配(Hash Slot 到 Redis Instance)

Redis Cluster 提供了两种映射分配方式
  • 由 Redis 自动平均分配(使用 cluster create 命令创建集群),即每个实例可以分配到 16384/N 个哈希槽(N为实例个数)
  • 由管理员手动分配(使用 cluster meet 命令创建集群)。

自动分配

  1. 在运行前,先定义哈希槽与 Redis 实例的对应关系
    1. 使用 cluster create 命令创建集群,Redis 会自动把这些槽平均分布在集群实例上。如果集群中有 N 个实例,那么每个实例上的槽个数为 16384/N 个。
  1. 在运行时,根据键值对的 key,按照规定的算法,将键值对的 key 映射到哈希槽,进而进入到具体的 Redis 实例中。

手动分配

示意图中的切片集群一共有 3 个实例,同时假设有 5 个哈希槽。手动分配哈希槽命令如下:
notion image
💡
注意:在手动分配哈希槽时,需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作。 以下只是举了一个例子,只有 5 个哈希槽。

客户端如何定位数据?

  1. Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散。当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。
  1. 客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端。
  1. 客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。
  1. 当客户端请求键值对时,会先计算键所对应的哈希槽,然后根据本地缓存的哈希操信息,给相应的实例发送请求了。

引发实例和哈希槽的对应关系变化的操作

  • 在集群中,实例有新增或删除,Redis 需要重新分配哈希槽
  • 为了负载均衡,Redis 需要把哈希槽在所有实例上重新分布一遍
Redis Cluster 方案提供了一种重定向机制,来解决实例和哈希槽对应关系变化导致客户端缓存的分配信息和最新的分配信息不一致的问题。

客户端给一个实例发送数据读写操作,遇到的两种情况

新的关系和数据已经同步完成

根据原来客户端缓存对应关系信息,计算得到实例上是没有这个键值对映射的哈希槽(数据已经发生了迁移)。这个实例会给客户端返回 MOVED 报错信息,其中包含了新实例的访问地址。
其中 MOVED 命令表示,客户端请求的键值对所在的哈希槽 13320,实际是在 172.16.19.5 这个实例上。
之后,客户端再给一个新实例发送操作命令,并更新本地缓存
notion image

新的关系已经确认,数据正在同步

也就是 Slot 中的数据只有一部分迁移到了实例,还有部分数据没有迁移。在这种迁移部分完成的情况下,客户端就会收到一条 ASK 报错信息:
ASK 命令表示客户端请求的键值对所在的哈希槽 13320,在 172.16.19.5 这个实例上,但是这个哈希槽正在迁移。
此时,客户端需要先给 172.16.19.5 这个实例发送一个 ASKING 命令。这个命令的意思是,让这个实例允许执行客户端接下来发送的命令。
然后,客户端再向这个实例发送 GET 命令,以读取数据。
因为没有更新本地缓存,如果客户端再次请求该 Slot 中的数据,还需要走一遍上述的流程。也就是说,ASK 命令的作用只是让客户端能给新实例发送一次请求。
notion image
💡
注:和 MOVED 命令不同,ASK 命令并不会更新客户端缓存的哈希槽分配信息.
  • MOVE 命令是在数据迁移完毕后被响应,客户端会更新本地缓存。
  • ASK 命令是在数据迁移中被响应,客户端不会更新本地缓存。

Redis 配置

📎 参考

  • 极客时间,蒋德钧《Redis核心技术与实战》
 
  • Redis
  • Redis 数据结构哨兵集群
    Loading...
    目录