Redis中的集群(五)

集群

在集群中执行命令

MOVED错误。

当节点发现键所在的槽并非由自己负责处理的时候,节点就会向客户端返回一个MOVED错误,指引客户端转向至正在负责槽的节点,MOVED错误的格式为:

c 复制代码
MOVED <slot> <ip>:<port>

其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地址和端口号。例如错误:

c 复制代码
MOVED 10086 127.0.0.1:7002

表示槽10086正由IP地址为127.0.0.1,端口号为7002的节点负责,又例如错误:

c 复制代码
MOVED 789 127.0.0.1:7000

表示槽789正由IP地址为127.0.0.1,端口号为7000的节点负责。

当客户端接收到节点返回的MOVED错误时,客户端会根据MOVED错误中提供的IP地址和端口号,转向至负责处理槽slot的节点,并向该节点重新发送之前想要执行的命令。以前面的客户端从节点7000转向至7001的情况作为例子:

c 复制代码
127.0.0.1:7000> SET msg "happy new year!"
-> Redirected to slot[6257] located at 127.0.0.1:7001
OK

如图所示展示了客户端向节点7000发送SET命令,并获得MOVED错误的过程

客户端根据MOVED错误,转向至节点7001,并重新发送SET命令的过程

一个集群客户端通常会与集群中的多个节点创建套接字连接,而所谓的节点转向实际上就是换一个套接字来发送命令。如果客户端尚未与想要转向的节点创建套接字连接,那么客户端会先根据MOVED错误提供的IP地址和端口号来连接节点,然后再进行转向

被隐藏的MVOED错误

集群模式的redis-cli客户端在接收到MOVED错误时,并不会打印出MOVED错误,而是根据MOVED错误自动进行节点转向,并打印出转向信息,所以是看不见节点返回的MOVED错误的

c 复制代码
redis-cli -c -p 7000 #集群模式

127.0.0.1:7000> SET msg "happy new year!"
-> Redirected to slot[6257] located at 127.0.0.1:7001
OK

但是如果,使用单机(stand alone)模式的redis-cli客户端,再次向节点7000发送相同的命令,那么MOVED错误就会被打印出来:

c 复制代码
redis-cli -p 7000 # 单机模式
127.0.0.1:7000> SET msg "happy new year!"
(error) MOVED 6257 127.0.0.1:7001

这是因为单机模式的redis-cli客户端不清除MOVED错误的作用,所以它只会直接将MOVED错误直接打印出来,而不会进行自动转向。

节点数据库的实现

集群节点保存键值对以及键值对过期时间的方式,与单机模式下保存键值对以及对过期时间的方式完全相同。节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库,而单机Redis服务器则没有这一限制。

slots_to_keys跳跃表每个节点的分值(score)都是一个槽号,而每个节点的成员(member)都是一个数据库键:

  • 1.每当节点往数据库中添加一个新的键值对时,节点就会将这个键以及键的槽号关联到slots_to_keys跳跃表
  • 2.当节点删除数据库中某个键值对时,节点就会在slots_to_keys跳跃表接触被删除键与槽号的关联
例子
  • 举个例子。如图展示了节点7000的数据库状态,数据库中包含列表键"lst",哈希键"book",以及字符串键"date",其中键"lst"和键"book"带有过期时间。另外除了将键值对保存在数据库里面之外,节点还会用clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系:
c 复制代码
typedef struct clusterState {
//...
zskiplist *slot_to_keys;
// ...
}clusterState;
  • 举个例子。对于上图所示的数据库,节点7000将创建类似如图所示的slots_to_keys跳跃表:
    1.键"book"所在跳跃表节点的分值为1337.0,这表示键"book"所在的槽位1337
    2.键"date"所在跳跃表节点的分值位2022.0,这表示键"date"所在的槽为2022
    3.键"lst"所在跳跃表节点的分值为3347.0,这表示键"lst"所在的槽为3347

通过在slots_to_keys跳跃表中记录各个数据库键所属的槽,节点可以很方便地对属于某个或某些槽的所有数据库进行批量操作,例如命令CLUSTER GETKEYSINSLOT 命令可以返回最多count个属于槽slot的数据库键,而这个命令就是通过遍历slots_to_keys跳跃表来实现的

重新分片

Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

例子

  • 举个例子。对于之前提到的7000、7001、7002三个节点的集群来说,我们可以向这个集群中添加一个IP为127.0.0.1,端口号为7003的节点
c 复制代码
redis-cli -c -p 7000
127.0.0.1:7000> CLUSTER MEET 127.0.0.1 7003
OK

可以用CLUSTER NODES进行查看各个节点之前所负责的槽的区间

重新分片的实现原理

Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。redis-trib对集群的单个槽slot进行重新分片的步骤如下:

  • 1.redis-trib对目标节点发送CLUSTER SETSLOT IMPORTING <source_id>命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对
  • 2.redis-trib对源节点发送CLUSTER SETSLOT MIGRATING <target_id>命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点
  • 3.redis-trib向源节点发送CLUSTER GETKEYSINSLOT 命令,获得最多count个属于槽slot的键值对的键名(key name)
  • 4.对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE <target_ip> <target_port> <key_name> - 命令,将被选中的键原子地从源节点迁移至目标节点
  • 5.重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。每次迁移的过程如图所示
  • 6.redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT NODE <target_id>命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会直到槽slot已经指派给了目标节点

如果重新分片涉及多个槽,那么redis-trib将对每个给定的槽分别执行上面给出的步骤,流程图如图所示

相关推荐
bxnms.2 分钟前
Redis存储原理
数据库·redis·缓存
gergul5 分钟前
lettuce引起的Redis command timeout异常
数据库·redis·缓存·jedis·lettuce·redis timeout
争不过朝夕,又念着往昔6 分钟前
Redis中Hash(哈希)类型的基本操作
数据库·redis·缓存·哈希算法
星眺北海6 分钟前
【redis】常用数据类型及命令
数据库·redis·缓存
长安初雪9 分钟前
Java客户端SpringDataRedis(RedisTemplate使用)
java·redis
技术不支持9 分钟前
WSL2+Ubuntu 22.04搭建Qt开发环境+中文输入法
linux·运维·服务器·qt·ubuntu
_infinite_14 分钟前
Ubuntu 与Uboot网络共享资源
linux·服务器·ubuntu
繁依Fanyi28 分钟前
使用 Spring Boot + Redis + Vue 实现动态路由加载页面
开发语言·vue.js·pytorch·spring boot·redis·python·算法
aloha_78934 分钟前
B站宋红康JAVA基础视频教程(chapter14数据结构与集合源码)
java·数据结构·spring boot·算法·spring cloud·mybatis
尘浮生42 分钟前
Java项目实战II基于Java+Spring Boot+MySQL的洗衣店订单管理系统(开发文档+源码+数据库)
java·开发语言·数据库·spring boot·mysql·maven·intellij-idea