redis学习笔记(六)分片集群

2021-02-24 11:16:18 浏览数 (1)

redis高可用的方式的其中一种是主从集群方式,主要是为了读写分离来分担读的压力,每个节点数据都是一致的。

另一种方式是分片集群的方式,主要讲多个redis实例组成一个集群,将redis的数据划分多份,每一份由一个实例来保存。

主从集群模式实例图如下所示:

分片集群模式实例图如下所示:

一、redis如何应对数据量增多的扩展方式

· 纵向扩展

升级单个redis实例的配置,比如增加实例内存、增加磁盘容量、采用高配CPU。

优点:

1、加机器的配置的实现方式比较简单。

2、在不要求持久化保存redis数据时,选择纵向扩展是一个非常好的选择。

缺点:

1、意味着数据量的增加,在主从同步时,或者使用RDB持久化时,从主线程fork子进程时由于数据量过大会导致主线程阻塞。

2、纵向扩展会受到硬件和成本的限制。

· 横向扩展

即增加当前redis分片实例的个数,将数据分成多分,保存在不同的分片实例中。

优点:

1、与纵向扩展相比,扩展性更好,如果需要保存更多的数据只是要增加redis的分片实例即可。

2、在面向百万、千万级别的用户规模时,横向扩展的切片集群是一个非常好的选择。

缺点:

1、需要涉及多个分片实例间的管理问题,实现困难。

二、redis数据和分片实例的对应关系

· 数据如何分部在切片实例中

redis3.0开始,官方提供了redis cluster的方案,来实现切片集群。redis cluster中采用了哈希槽来处理数据和实例的映射,即一个切片集群共有16384个哈希槽,每个缓存数据的key都会被映射到哈希槽中。

哈希槽默认会被平均分布在切片集群中,即假设集群中有16个切片实例,那么表示每个实例中有16384/16 = 1024个哈希槽。

也可以采用命令的方式手动分配哈希槽:

redis-cli -h <IP> -p <port> cluster addslots {0..100}

表示将指定ip和端口的实例分配0~100的哈希槽。并且在手动分配哈希槽的时候需要注意,需要把16384个哈希槽全部分配完成,否则集群无法正常工作。

我们在集群对key进行操作时,实际上会映射到某一个哈希槽中最后映射到对应的实例。映射的规则:首先会将该key根据CRC16算法计算一个16位的值,然后将这个值对16384进行取模,得到最终的值就是对应编号的哈希槽。通过该哈希槽对应到分片实例中。

三、redis cluster的重定向机制

在分片集群中,每个实例和哈希槽的映射关系不是固定的,如:

1、分片集群中的实例出现了新增或者删除,就需要重新分配哈希槽。

2、为了负载均衡,redis需要把哈希槽在所有实例上重新分布一遍。

· 重定向 - MOVED

MOVED命令表示对实例进行请求时,槽数据全部迁移到新实例后的重定向响应。

由于客户端无法主动感知实例上哈希槽的变化,因此需要使用redis cluster的重定向机制,即客户端给某个实例发送请求命令时,如果出现MOVED响应结果(MOVED <哈希槽> <new IP>:<new port>)时,客户端会再次向新的实例发送请求命令。

比如:有三个实例(实例1:192.168.23.01:6379;实例2:192.168.23.02:6379;实例3:192.168.23.03:6379)。

实例1对应着0~5000的哈希槽,实例2对应着5001~10000的哈希槽,实例3对应着10001~16384的哈希槽。

在实例新增或者删除之前,对实例1,请求get "test"命令,会返回对应的结果值。

由于新增了实例或者删除了实例,哈希槽中的一些数据如key为“test”的数据对应的第5000个槽,被迁移到了实例2中。

那么客户端再次对实例1执行get "test"命令时,会收到重定向指令即:(error) MOVED 5000 192.168.23.02:6379。

客户端收到MOVED命令后,客户端会再次向实例2发送请求,并更新客户端本地缓存中维护的哈希槽和实例的映射关系。

· 重定向 - ASK

ASK命令表示对实例进行请求时,槽数据还未迁移到新实例后的重定向响应。

如果出现槽数据还在迁移中则会出现ASK响应,即(error)ASK <哈希槽> <new IP>:<new Port>。按照上面的例子要对第5000个哈希槽对应的实例1进行访问时,由于此时哈希槽正在从实例1迁移到实例2,并且没有结束。那么收到的重定向指令为:(error) ASK 5000 192.168.23.02:6379。

上述ask响应表示,第5000个哈希槽正在迁移,新的实例为192.168.23.02:6379,客户端收到ask响应会会继续请求新的实例。

ASK响应和MOVED响应不同的是,收到ask响应后虽然也和moved一样会继续请求新的实例,但是并不会更新客户端本地缓存中维护的哈希槽和实例的映射关系。

四、总结

虽然切片集群和主从集群都是高可用的几种方案,但是在应对数据量进行扩容时,虽然直接增加主内存的配置进行纵向扩展方便简单,但是会造成redis内存过大,导致fork时阻塞主线程性能变慢。因此在面向百万、千万量级的请求时,使用横向扩展的方式将键的hash值映射到哈希槽中再将哈希槽映射到实例中的方式对于集群的扩展性会更好。

问题一:

redis cluster方案通过哈希槽的方式把键值对分配到不同的实例上,这个过程需要对键值对的key做CRC计算,然后再和哈希槽做映射。为什么不用一个表直接把键值对和实例的对应关系记录下来,这样就不用计算key和哈希槽的对应关系了,只用查表就行了,redis为什么不这样做?

如果使用表来记录键值对和实例的对应关系,一旦键值对和实例的映射关系发生了变化,就要修改表。如果是单线程操作,所有的操作串行执行性能会很慢。如果是多线程操作表,就要涉及到锁的开销。

如果数据量非常大的情况,使用表来记录,需要的存储空间也会非常的大。

基于对键值对的key做哈希槽计算,虽然也要记录哈希槽和实例的对应关系,但是哈希槽的个数是固定的16384,远远小于键值对的个数,会比表的记录有着更小的开销。

参考资料 - 《Redis核心技术与实战》(切片集群)

0 人点赞