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将对每个给定的槽分别执行上面给出的步骤,流程图如图所示

相关推荐
九月十九几秒前
AviatorScript用法
java·服务器·前端
翻晒时光8 分钟前
深入解析Java集合框架:春招面试要点
java·开发语言·面试
峰子201214 分钟前
B站评论系统的多级存储架构
开发语言·数据库·分布式·后端·golang·tidb
sin220119 分钟前
MyBatis-Plus的插件
java·mybatis
ICT系统集成阿祥23 分钟前
科普篇 | “机架、塔式、刀片”三类服务器对比
运维·服务器
小丁爱养花26 分钟前
Spring MVC:综合练习 - 深刻理解前后端交互过程
java·spring·mvc
五行星辰44 分钟前
Java 生成 PDF 文档 如此简单
java·pdf·maven
progrmmmm1 小时前
k8s使用nfs持久卷
linux·服务器·kubernetes·k8s·运维开发
无空念1 小时前
Linux - 五种常见I/O模型
linux·运维·服务器
旦沐已成舟1 小时前
K8S-标签管理,探针,名称空间,rc控制器,svc服务发现
服务器·云原生·kubernetes