Redis:切片集群

切片集群,也叫分片集群,就是指启动多个Redis 实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。

在实际应用Redis时,保存大量数据的情况通常是无法避免的。而切片集群,就是一个非常好的解决方案。

如何保存更多数据

Redis 应对数据量增多的两种方案:纵向扩展(scale up)和横向扩展(scale out)

  • 纵向扩展:升级单个Redis 实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的CPU。
  • 横向扩展:横向增加当前Redis 实例的个数。

这两种方式的优缺点分别是什么呢?

纵向扩展的好处是,实施起来简单、直接。不过这个方案面临两个潜在的问题。

  • 第一个问题是,当使用RDB对数据进行持久化时,如果数据量增加,需要的内存也会增加,主线程 fork 子进程时就可能会阻塞。如果不要求持久化保存 Redis 数据,纵向扩展会是一个不错的选择。

  • 第二个问题:纵向扩展会受到硬件和成本的限制。

与纵向扩展相比,横向扩展是一个扩展性更好的方案。这是因为,要想保存更多的数据,采用这种方案的话,只用增加Redis的实例个数就行了,不用担心单个实例的硬件和成本限制。在面向百万、千万级别的用户规模时,横向扩展的Redis切片集群会是一个非常好的选择。

不过,在只使用单个实例的时候,数据存在哪儿,客户端访问哪儿,都是非常明确的,但是,切片集群不可避免地涉及到多个实例的分布式管理问题。要想把切片集群用起来,就需要解决两大问题:

  • 数据切片后,在多个实例之间如何分布?
  • 客户端怎么确定想要访问的数据在哪个实例上?

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

在切片集群中,数据需要分布在不同实例上,数据和实例之间如何对应呢?

与 Redis Cluster方案有关。先弄明白切片集群和 Redis Cluster 的联系与区别。

切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。在 Redis 3.0之前,官方并没有针对切片集群提供具体的方案。从3.0开始,官方提供了一个名为 Redis Cluster 的方案,用于实现切片集群。Redis Cluster方案中规定了数据和实例的对应规则。

Redis Cluster方案采用哈希槽(Hash Slot),来处理数据和实例之间的映射关系。在 Redis Cluster方案中,一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中。

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

这些哈希槽又是如何被映射到具体的 Redis 实例上的呢?

在部署Redis Cluster方案时,可以使用 cluster create 命令创建集群,此时,Redis 会自动把这些槽平均分布在集群实例上。例如,如果集群中有N个实例,那么,每个实例上的槽个数为16384/N个。

也可以使用 cluster meet 命令手动建立实例间的连接,形成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数。

比如,假设集群中不同 Redis 实例的内存大小配置不一,如果把哈希槽均分在各个实例上,在保存相同数量的键值对时,和内存大的实例相比,内存小的实例就会有更大的容量压力。遇到这种情况时,可以根据不同实例的资源配置情况,使用cluster addslots命令手动分配哈希槽。

在手动分配哈希槽时,需要把16384个槽都分配完,否则 Redis集群无法正常工作。

**通过哈希槽,切片集群就实现了数据到哈希槽、哈希槽再到实例的分配。**但是,即使实例有了哈希槽的映射信息,客户端又是怎么知道要访问的数据在哪个实例上呢?

客户端如何定位数据

在定位键值对数据时,它所处的哈希槽是可以通过计算得到的,这个计算可以在客户端发送请求时来执行。但是,要进一步定位到实例,还需要知道哈希槽分布在哪个实例上。

一般来说,**客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端。**但是,在集群刚刚创建的时候,每个实例只知道自己被分配了哪些哈希槽,是不知道其他实例拥有的哈希槽信息的。

那么客户端为什么可以在访问任何一个实例时,都能获得所有的哈希槽信息呢?这是因为,Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散。当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。

**客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。**当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给相应的实例发送请求了。

但是,在集群中,实例和哈希槽的对应关系并不是一成不变的,最常见的变化有两个:

  • 在集群中,实例有新增或删除,Redis需要重新分配哈希槽;
  • 为了负载均衡,Redis需要把哈希槽在所有实例上重新分布一遍。

此时,实例之间还可以通过相互传递消息,获得最新的哈希槽分配信息,但是,客户端是无法主动感知这些变化的。这就会导致,它缓存的分配信息和最新的分配信息就不一致了,怎么解决?

Redis Cluster方案提供了一种重定向机制,即客户端给一个实例发送数据读写操作时,这个实例上并没有相应的数据,客户端要再给一个新实例发送操作命令。

那客户端又是怎么知道重定向时的新实例的访问地址呢?当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端返回下面的 MOVED 命令响应结果,这个结果中就包含了新实例的访问地址。

GET hello:key
(error) MOVED 13320 172.16.19.5:6379

MOVED命令表示,客户端请求的键值对所在的哈希槽13320,实际是在172.16.19.5这个实例上。通过返回的MOVED命令,就相当于把哈希槽所在的新实例的信息告诉给客户端了。这样一来,客户端就可以直接和172.16.19.5连接,并发送操作请求了。MOVED命令会更改本地缓存,让后续所有命令都发往新实例。

图示MOVED重定向命令的使用方法。可以看到,由于负载均衡,Slot 2中的数据已经从实例2迁移到了实例3,但是,客户端缓存仍然记录着“Slot 2在实例2”的信息,所以会给实例2发送命令。实例2给客户端返回一条MOVED命令,把 Slot 2的最新位置(也就是在实例3 上),返回给客户端,客户端就会再次向实例3发送请求,同时还会更新本地缓存,把 Slot 2与实例的对应关系更新过来。

在这里插入图片描述

需要注意的是、在上图中,当客户端给实例2发送命令时,Slot 2中的数据已经全部迁移到了实例3。在实际应用时,如果Slot 2中的数据比较多,就可能会出现一种情况:客户端向实例2发送请求,但此时,Slot 2中的数据只有一部分迁移到了实例3,还有部分数据没有迁移。在这种迁移部分完成的情况下,客户端就会收到一条ASK报错信息,如下所示:

GET hello : key
(error) ASK 13320 172.16.19.5:6379

这个结果中的 ASK 命令就表示,客户端请求的键值对所在的哈希槽13320,在172.16.19.5这个实例上,但是这个哈希槽正在迁移。此时,客户端需要先给172.16.19.5这个实例发送一个ASKING命令。这个命令的意思是,让这个实例允许执行客户端接下来发送的命令。然后,客户端再向这个实例发送GET命令,以读取数据。

在下图中,Slot 2正在从实例2往实例3迁移,key1和 key2已经迁移过去,key3和 key4还在实例2。客户端向实例2请求key2后,就会收到实例2返回的ASK命令。

ASK命令表示两层含义:第一,表明Slot数据还在迁移中;第二,ASK命令把客户端所请求数据的最新实例地址返回给客户端,此时,客户端需要给实例3发送ASKING命令,然后再发送操作命令。

在这里插入图片描述

**和MOVED命令不同,ASK命令并不会更新客户端缓存的哈希槽分配信息。**所以,在上图中,如果客户端再次请求Slot 2中的数据,它还是会给实例2发送请求。这也就是说,ASK命令的作用只是让客户端能给新实例发送一次请求,而不像MOVED命令那样,会更改本地缓存,让后续所有命令都发往新实例。

为什么 Redis 不直接用一个表,把键值对和实例的对应关系记录下来

如果使用表记录键值对和实例的对应关系,一旦键值对和实例的对应关系发生了变化(例如实例有增减或者数据重新分布),就要修改表。如果是单线程操作表,那么所有操作都 要串行执行,性能慢;如果是多线程操作表,就涉及到加锁开销。此外,如果数据量非常大,使用表记录键值对和实例的对应关系,需要的额外存储空间也会增加。 基于哈希槽计算时,虽然也要记录哈希槽和实例的对应关系,但是哈希槽的个数要比键值对的个数少很多,无论是修改哈希槽和实例的对应关系,还是使用额外空间存储哈希槽和实例的对应关系,都比直接记录键值对和实例的关系的开销小得多。

参考资料

《Redis 45讲》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值