Redis的切片集群

要用Redis保存5000万个键值对,每个键值对大约512B,这些键值对所占的内存空间大约是25GB(5000万 * 512B)。需要选择一台32GB内存的云主机来部署Redis,因为32GB的内存可以保存所有数据,而且还留有7GB来保证系统运行。

同时还需要采用RDB对数据做持久化,但是在使用过程中,Redis的响应有时会非常慢。使用INFO命令查看Redis的latest_fork_usec指标值(最近一次fork的耗时),结果显示这个指标值特别高,快到秒级别了。

在使用RDB进行持久化时,Redis会fork子进程来完成,fork操作的用时和Redis的数据量是正相关的,而fork在执行时会阻塞主线程。数据量越大,fork操作造成的主线程阻塞时间越长。所以在使用RDB对25GB数据进行持久化时,数据量较大,后台运行的子线程在fork创建时阻塞了主线程,于是就导致Redis响应变慢了。

Redis的切片集群可以保存大量数据,而且对Redis主线程的阻塞影响较小。切片集群也叫分片集群,就是指启动多个Redis实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。

如果把25GB数据平均分成5份,使用5个实例来保存,每个实例只需要保存5GB数据。实例在为5GB数据生成RDB时,数据量就小了很多,fork子进程一般不会给主线程带来较长时间的阻塞。

在刚刚的案例中,为了保存大量数据,使用了大内存云主机和切片集群两种方法,实际上这种方法分别对应着Redis应对数据量增多的两种方案:

  • 纵向扩展:升级单个Redis实例的资源配置,包括增加内存容量、增加磁盘容量、使用更高配置的CPU。实施起来简单、直接。但是如果需要持久化,主线程fork子进程就可能会阻塞。纵向扩容会受到硬件限制,比如想要扩充到1T内存,就会受到硬件限制了。
  • 横向扩展:横向增加当前Redis实例的个数。在面对百万、千万级别的用户规模时,横向扩展的Redis切片集群会是一个非常好的选择。

想要把切片集群用起来,还需要解决两大问题:

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

切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。从Redis3.0开始,官方提供了一个名为Redis Cluster的方案,用于实现切片集群。Redis Cluster方案中就规定了数据和实例的对应规则。

Redis Cluster方案采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系。在Redis Cluster方案中,一个切片集群共有16384个哈希槽。

在部署Redis Cluster方案时,可以使用cluster create命令创建集群,此时,Redis会自动把这些哈希槽平均分布在集群实例上。

如果集群中不同Redis实例的内存大小配置不一,也可以使用cluster meet命令手动建立实例间的连接,形成集群,再用cluster addslots命令,指定每个实例上的哈希槽个数。

假设集群中一共有3个实例,有5个哈希槽。可以通过以下命令手动分配哈希槽:

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.4 -p 6379 cluster addslots 4

数据、哈希槽、实例三者映射分布情况如下:

在手动分配哈希槽时,需要把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连接,并发送操作请求了。

上图中,客户端给实例2发送命令时,Slot2中的数据已经全部迁移到了实例3,如果Slot2中的数据比较多,可能Slot2中的数据只有一部分迁移到了实例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命令,以读取数据。

和MOVED命令不同,ASK命令并不会更新客户端缓存的哈希槽分配信息。所以,上图中,如果客户端再次请求Slot2中的数据,他还是会给实例2发送请求。ASK命令的作用只是让客户端能给新实例发送一次请求,不会更改本地缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无绪听雨眠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值