切片集群,也叫分片集群,就是指启动多个Redis实例组成一个集群,然后按照一定规则,将收到的数据划分成多份,每一份用一个实例来保存。
使用场景,将25GB数据保存有两种方案:
切片集群,也叫分片集群,就是指启动多个 Redis 实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。回到我们刚刚的场景中,如果把 25GB 的数据平均分成 5 份(当然,也可以不做均分),使用 5 个实例来保存,每个实例只需要保存 5GB 数据。如下图所示:
多实例保存数据切片,可以保存25GB数据又能避免fork子进程阻塞主线程导致响应变慢。
目的:保存大量数据
方法:扩大Redis实例内存(纵向扩展 scale up)和切片集群(横向扩展 scale out)两种方法。
纵向扩展
横向扩展是一个扩展性更好的方案,只需要增加Redis实例个数,不用担心单个实例的硬件和成本限制,面向百万级,千万级的用户规模时,横向扩展Redis切片集群会是很好的选择。
切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案。
Redis 3.0以后官方提供Redis cluster方案,用于实现切片集群。
Redis Cluster方案采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系。一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据他的key映射到一个哈希槽中。
我们可以使用cluster meet命令手动建立实例间的连接,形成集群,在使用cluster addslots命令,指定每个实例上的哈希槽个数。
集群中不同Redis实例的内存大小配置不一,如果把哈希槽均分在各个实例上,在保存相同数量的键值对时,和内存大的实例相比,内存小的实例会有更大的容量压力。这时候可以根据不同实例的资源配置情况,使用cluster addslots命令手动分配哈希槽。
切片集群一共有3个实例,同事假设有5个哈希槽,可以通过下面命令手动分配哈希槽:
实例1保存哈希槽0和1,实例2保存哈希槽2和3,实例3保存哈希槽4。
redis-cli -h 127.0.0.1 –p 6379 cluster addslots 0,1 redis-cli -h 127.0.0.1 –p 6380 cluster addslots 2,3 redis-cli -h 127.0.0.1 –p 6381 cluster addslots 4
手动分配哈希槽时,需要把16384个槽都分配完,否则Redis集群无法正常工作。
在定位键值对数据时,他所在的哈希槽是可以计算获得的,这个计算是在客户端发送请求时来执行。但是,要进一步定位到实例,还需要知道哈希槽分布在哪个实例上。
客户端无法通过互相传递消息,获得最新的哈希槽分配信息,但是客户端无法主动感知。
他缓存的分配信息和最新的分配信息不一致怎么办?
客户端给一个实例发送数据读写操作,实例上并没有相应的数据,客户端要在给一个新的实例发送操作命令。
客户端将一个键值对的操作请求发给一个实例的时候,如果这个实例没有这个键值对映射的哈希槽,那么这个实例会给客户端返回move命令响应结果,这个结果就包含了新实例的访问地址。
GET hello:key (error) MOVED 13320 172.16.19.5:6379
MOVED 命令表示,客户端请求的键值对所在的哈希槽 13320,实际是在 172.16.19.5 实例上。
Slot 2 中的数据已经全部迁移到了实例 3的情况下才会move
如果slot2中的数据多,客户向实例2发送请求时,此时slot2中的数据只有一部分迁移到了实例3,还有部分数据没有迁移。这种迁移部分完成情况下,客户端会收到一条ASK报错信息。
GET hello:key (error) ASK 13320 172.16.19.5:6379
例子:
ASK 命令并不会更新客户端缓存的哈希槽分配信息。
如果客户端再次请求 Slot 2 中的数据,它还是会给实例 2 发送请求。这也就是说,ASK 命令的作用只是让客户端能给新实例发送一次请求,而不像 MOVED 命令那样,会更改本地缓存,让后续所有命令都发往新实例。
Redis Cluster方案通过哈希槽的方式把键值对分配到不同的实例上,这个过程需要对键值对key进行CRC计算,然后与哈希槽做映射,这样做有什么好处?如果直接用一个哈希表把键值对和实例的对应关系记录下来,这样用的时候直接查表,为啥不可以呢?