Redis核心技术与实战-学习笔记(九):切片集群

一.切片集群

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

使用场景,将25GB数据保存有两种方案:

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

Redis核心技术与实战-学习笔记(九):切片集群

多实例保存数据切片,可以保存25GB数据又能避免fork子进程阻塞主线程导致响应变慢。

二.如何保存更多数据?

目的:保存大量数据

方法:扩大Redis实例内存(纵向扩展 scale up)和切片集群(横向扩展 scale out)两种方法。

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

纵向扩展

  • 优点:简单,直接
  • 缺点:
  •         一.使用RDB对数据进行持久化时,如果数据量增加,会导致内存也增加,主线程fork子进程会阻塞。如果不要求持久化保存Redis数据,那么纵向扩展也是不错的选择。 
  •         二.硬件成本限制。

横向扩展是一个扩展性更好的方案,只需要增加Redis实例个数,不用担心单个实例的硬件和成本限制,面向百万级,千万级的用户规模时,横向扩展Redis切片集群会是很好的选择。

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

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

       Redis 3.0以后官方提供Redis cluster方案,用于实现切片集群。 

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

  1. 首先根据键值对的key,按照CRC16算法计算一个16bit的值;然后再用这个16bit值对16384取模,得到0~16384范围内的模数,每个模数代表一个相应编号的哈希槽。 
  2. 哈希槽如何映射到具体的Redis实例:在不是Redis Cluster方案时,可以使用cluster create命令创建集群,此时Redis会自动把这些槽平均分布在集群实例上。如果集群中有N个实例,但是每个实例上的槽的个数为16384/N个。

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

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

Redis核心技术与实战-学习笔记(九):切片集群

 切片集群一共有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集群无法正常工作。

客户端如何定位数据?

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

  1.  客户端和集群实例建立连接后,实例会把哈希槽的分配信息发送给客户端。
  2. 集群刚建立时候,每个集群只知道自己被分配了那些哈希槽,不知道其他实例所对应的哈希槽信息。
  3.   Redis实例会把自己的哈希槽信息发送给与它相连的其他实例,来完成哈希槽分配信息的扩散。当实例之间互相连接以后,所有实例都知道所有哈希槽的映射关系。客户端在访问任何一个实例时,都能获得所有的哈希槽信息。
  4. 客户端收到哈希槽信息后,会把哈希槽信息缓存在本地,当客户端请求键值对时,会先计算键所在哈希值,这样就可以直接给对应的实例发送请求。

Redis 需要重新分配哈希槽

     客户端无法通过互相传递消息,获得最新的哈希槽分配信息,但是客户端无法主动感知。

     他缓存的分配信息和最新的分配信息不一致怎么办?

重定向

       客户端给一个实例发送数据读写操作,实例上并没有相应的数据,客户端要在给一个新的实例发送操作命令。

      客户端将一个键值对的操作请求发给一个实例的时候,如果这个实例没有这个键值对映射的哈希槽,那么这个实例会给客户端返回move命令响应结果,这个结果就包含了新实例的访问地址。

     


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

   MOVED 命令表示,客户端请求的键值对所在的哈希槽 13320,实际是在 172.16.19.5 实例上。

  1.   MOVE重定向命令的使用方法: 
  2.  由于负载均衡,slot2中的数据已经从实例2迁移到实例3
  3. 客户端缓存仍然记录着slot2在实例2的信息,所以会给实例2发送命令。
  4. 实例2给客户端返回一条move命令,把slot2的最新位置(实例3),返回给客户端。
  5. 客户端会再次向实例3发送请求,同时还会更新本地缓存,把slot2与实例的对应关系更新过来。

Slot 2 中的数据已经全部迁移到了实例 3的情况下才会move

ASK

如果slot2中的数据多,客户向实例2发送请求时,此时slot2中的数据只有一部分迁移到了实例3,还有部分数据没有迁移。这种迁移部分完成情况下,客户端会收到一条ASK报错信息。


GET hello:key
(error) ASK 13320 172.16.19.5:6379
  1. 这个结果中的 ASK 命令就表示 客户端  请求键值对所对应的哈希槽13320,在实例3上,但是这个哈希槽正在迁移至实例3.
  2. 此时客户端需要先给实例3发送一个ASKING命令:让这个实例允许执行客户端接下来的命令。
  3. 客户端在向这个实例发送GET命令,以读取这个数据。

例子:

  1. slot2正从实例2向实例3迁移,key1和key2已经迁移过去,key3和key4还在实例2中。
  2. 客户端向实例2请求key2后,会收到实例2返回的ASK命令。
  3.  ASK命令的两层含义:
  4.          表示slot数据还在迁移中。
  5. ASK命令把客户端所请求数据的最新实例地址返回给客户端
  6.  客户端需要给实例3发送ASKING命令,然后再发送操作命令。

ASK 命令并不会更新客户端缓存的哈希槽分配信息。

     如果客户端再次请求 Slot 2 中的数据,它还是会给实例 2 发送请求。这也就是说,ASK 命令的作用只是让客户端能给新实例发送一次请求,而不像 MOVED 命令那样,会更改本地缓存,让后续所有命令都发往新实例。

课后提问:

       Redis Cluster方案通过哈希槽的方式把键值对分配到不同的实例上,这个过程需要对键值对key进行CRC计算,然后与哈希槽做映射,这样做有什么好处?如果直接用一个哈希表把键值对和实例的对应关系记录下来,这样用的时候直接查表,为啥不可以呢?

  1. 整个集群存储的key数量过大,记录每个key与实例的映射表会非常庞大,无论是存储再服务端还是客户端都占用非常大的内存空间。
  2. Redis cluster采用无中心化模式,客户端在某个节点访问一个key,如果这个key不在这个节点上,这个节点需要有纠正客户端路由到正确节点的能力(move响应)。
  3. 节点之间需要交换路由表,每个阶段拥有完整的路由关系,如果存储的是key与实例的映射表,那么节点之间交换信息会变得无比庞大,消耗网络资源,同时内存占用资源也很大。
  4. 集群扩容,缩容,数据均衡时,节点之间会发生数据迁移,迁移时候需要修改每个key的映射关系,维护成本高。
  5. 中间增加一个哈希槽,可以解耦数据和节点的关系,key通过hash计算只需要关系映射到了哪个哈希槽,然后通过哈希槽与节点的映射表找到节点,相当于消耗了很少的cpu资源,让数据分配均匀,让映射表变小,有利于客户端和服务端保存,节点之间交换信息更轻量。
  6. 当集群在扩容、缩容、数据均衡时,节点之间的操作例如数据迁移,都以哈希槽为基本单位进行操作,简化了节点扩容、缩容的难度,便于集群的维护和管理。

    

上一篇:Redis——list相关命令


下一篇:php – 需要一些帮助来添加一些验证码联系表单