Redis Cluster 集群

我曾遇到过这么一个需求:要用Redis保存5000万个键值对,每个键值对大约是512B,为了能快速部署并对外提供服务,我们采用云主机来运行Redis实例,那么,该如何选择云主机的内存容量呢?

我粗略地计算了一下,这些键值对所占的内存空间大约是25GB(5000万*512B)。

所以,当时,我想到的第一个方案就是选择一台32GB内存的云主机来部署Redis。因为32GB的内存能保存所有数据,而且还留有7GB,可以保证系统的正常运行。

同时,我还采用RDB对数据做持久化,以确保Redis实例故障后,还能从RDB恢复数据。但是,在使用的过程中,发现Redis的响应有时会非常慢。后来,我们使用 INFO 命令查看Redis的 latest_fork_usec 指标值(表示最近一次fork的耗时),结果显示这个指标值特别高,快到秒级别了。

这跟Redis的持久化机制有关系。

在使用RDB进行持久化时,Redis会使用 fork 子进程来完成,fork 操作的用时和Redis的数据量是正相关的,而fork 在执行时会阻塞主线程。数据量越大,fork 操作造成的主线程阻塞的时间越长。

所以,在使用RDB对25GB的数据进行持久化时,数据量较大,后台运行的子进程,在fork创建时阻塞了主线程,于是就导致Redis响应变慢了。

看来,这个方案显然是不可行的,我们必须要寻找其他的方案。

这个时候,注意到了Redis的切片集群。虽然组建切片集群比较麻烦,但是它可以保存大量数据,而且对Redis主线程的阻塞影响较小。

切片集群,也叫分片集群,就是指启动多个Redis实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。回到我们刚刚的场景中,如果把25GB的数据平均分成5份(当然,也可以不做均分),使用5个实例来保存,每个实例只需要保存5GB数据,如下图所示

在这里插入图片描述

那么,在切片集群中,实例在为5GB数据生成RDB时,数据量就小了很多,fork 子进程一般不会给主线程,带来较长时间的阻塞。采用多个实例保存数据切片后,我们既能保存25GB数据,又避免了fork 子进程阻塞主线程,而导致的响应突然变慢。

在实际应用Redis时,随着用户或业务规模的扩展,保存大量数据的情况,通常是无法避免的。而切片集群,就是一个非常好的解决方案。

如何保存更多数据?

在刚刚的案例里,为了保存大量数据,我们使用了大内存云主机和切片集群两种方法。

实际上,这两种方法分别对应着Redis应对数据量增多的两种方案:纵向扩展(scale up)和横向扩展(scale out)。

  • 纵向扩展:升级单个Redis实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的CPU。就像下图中,原来的实例内存是8GB,硬盘是50GB,纵向扩展后,内存增加到24GB,磁盘增加到150GB。
  • 横向扩展:横向增加当前Redis实例的个数,就像下图中,原来使用1个8GB内存、50GB磁盘的实例,现在使用三个相同配置的实例。

在这里插入图片描述

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

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

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

不过,这时,你还要面对第二个问题:纵向扩展会受到硬件和成本的限制。这很容易理解,毕竟,把内存从32GB扩展到64GB还算容易,但是,要想扩充到1TB,就会面临硬件容量和成本上的限制了。

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

但是,切片集群不可避免地涉及到多个实例的分布式管理问题。要想把切片集群用起来,我们就需要解决两大问题

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

接下来,我们就一个个地解决。

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

在切片集群中,数据需要分布在不同实例上,那么,数据和实例之间如何对应呢?这和接下来要说的Redis Cluster方案有关了。不过,我们要先弄明白切片集群和Redis Cluster的联系与区别。

实际上,切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。

在Redis 3.0之前,官方并没有针对切片集群提供具体的方案。

从3.0开始,官方提供了一个名为Redis Cluster的方案,用于实现切片集群。Redis Cluster方案中就规定了数据和实例的对应规则。具体来说,Redis Cluster方案采用哈希槽,来处理数据和实例之间的映射关系。

在Redis Cluster方案中,一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的key,被映射到一个哈希槽中。

具体的映射过程分为两大步

在这里插入图片描述

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

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

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

举个例子,假设集群中不同Redis实例的内存大小配置不一,如果把哈希槽均分在各个实例上,在保存相同数量的键值对时,和内存大的实例相比,内存小的实例就会有更大的容量压力。

遇到这种情况时,你可以根据不同实例的资源配置情况,使用 cluster addslots 命令手动分配哈希槽。

在上图中的切片集群一共有3个实例,同时假设有5个哈希槽,我们首先可以通过cluster addslots 命令手动分配哈希槽。实例1保存哈希槽0和1,实例2保存哈希槽2和3,实例3保存哈希槽4。

redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1
redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3
redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4

在集群运行的过程中,key1和key2计算完CRC16值后,对哈希槽总个数5取模,再根据各自的模数结果,就可以被映射到对应的实例1和实例3上了。另外,有一点同学们一定要注意,在手动分配哈希槽时,需要把16384个槽都分配完,否则Redis集群无法正常工作。好了,通过哈希槽,切片集群就实现了数据到哈希槽、哈希槽再到实例的分配。

Docker 搭建 Redis Cluster 集群

Linux系统上搭建 Redis Cluster集群

了解更多可扫码关注公众号
在这里插入图片描述

posted on 2021-12-27 19:48  凝神遐想  阅读(254)  评论(0编辑  收藏  举报

导航