当前位置: 首页 > news >正文

Redis设计与实现第17章 -- 集群 总结1(节点 槽指派)

集群通过分片sharding来进行数据共享,并提供复制和故障转移功能。

17.1 节点

一个Redis集群通常由多个节点node组成,刚开始每个节点都是相互独立的,必须将各个独立的节点连接起来,才能构成一个包含多个节点的集群。通过CLUSTER MEET <ip><port>命令完成,向一个节点发送该命令,可以让node节点与ip/port所指定的节点进行握手handshake,当握手成功后,node节点就会将ip/port所指定的节点添加到节点当前所在的集群中。

17.1.1 启动节点

一个节点就是一个运行在集群模式下的Redis服务器,这个服务器会在启动时根据cluster-enabled配置选项是否为yes来决定是否开启服务器的集群模式。

节点会继续使用所有在单机模式中使用的服务器组件。

初次之后,只有集群模式下才会用到的数据,节点保存在了clusterNode/clusterLink/clusterState结构。

17.1.2 集群数据结构

clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、名字、配置纪元、IP地址和端口号。每个节点都使用一个clusterNode结构来记录自己的状态,并且也记录集群里其他节点的作用。

struct clusterNode{//创建节点的时间mstime_t ctime;//节点的名字,由40个十六进制字符组成//例如68eef66df23420a5862208ef5b1a7005b806f2ffchar name[REDIS_CLUSTER_NAMELEN];//节点标识//使用各种不同的标识值记录节点的角色(比如主节点或者从节点),//以及节点目前所处的状态(比如在线或者下线)。int flags;//节点当前的配置纪元,用于实现故障转移uint64 tconfigEpoch;//节点的IP地址Char ip[REDIS_IP_STR_LEN];//节点的端口号int port;//保存连接节点所需的有关信息clusterLink *link;

其中link结构保存了连接节点所需的有关信息,比如套接字描述符、输入缓冲区和输出缓冲区。

typedef struct clusterLink{//连接的创建时间mstime_t ctime;// TCP 套接字描述符int fd;//输出缓冲区,保存着等待发送给其他节点的消息(message)。sds sndbuf;
//输入缓冲区,保存着从其他节点接收到的消息。sds rcvbuf;//与这个连接相关联的节点,如果没有的话就为NULLstruct clusterNode *node;
}clusterLink;

redisClient结构和clusterLink结构都有自己的套接字描述符和输入、输出缓冲区,这两个结构的区别在于,redisClient结构中的套接字和缓冲区是用于连接客户端的,而clusterLink结构中的套接字和缓冲区则是用于连接节点的。

每个节点都保存一个clusterState结构,记录了在当前的视角下,集群目前所处的状态,例如集群是在线还是下线,包含多少个节点,当前的配置纪元。

typedef struct clusterstate//指向当前节点的指针clusterNode *myself;//集群当前的配置纪元,用于实现故障转移uint64 tcurrentEpoch;//集群当前的状态:是在线还是下线int state;//集群中至少处理着一个槽的节点的数量int size;//集群节点名单(包括myself节点)//字典的键为节点的名字,字典的值为节点对应的clusterNode结构dict *nodes;
}clusterState;

17.1.3 CLUSTER MEET命令的实现

通过向节点A发送CLUSTER MEET命令,客户端可以向接收命令的节点A将另一个节点B添加到节点A当前所在的集群里面

收到命令的节点A将与节点B进行握手:

  1. 节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里

  2. 之后,节点A会根据该命令给出的IP地址和端口号,向节点B发送一条MEET消息

  3. 如果一切顺利,节点B将接收到节点A发送的MEET消息,节点B会为节点A创建一个clusternode结构,并将该结构添加到自己的clusterState.nodes字典里

  4. 之后,节点B向节点A发送一条PONG消息

  5. 节点A收到以后,就知道节点B已经成功接收到了自己发送的MEET消息

  6. 节点A向节点B返回一条PING消息

  7. 节点B收到后,握手完成

之后,节点A会将节点B的信息通过Gossip协议传播给集群中其他节点,让其他节点也与节点B握手。

17.2 槽指派

Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被划分为16384个槽slot,数据库的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽

当数据库中的16384个槽都有节点在处理时,集群处于上线ok状态;否则,处于下线fail状态。

通过向节点发送CLUSTER ADDSLOTS命令,可以将一个或多个槽指派给节点负责,比如127.0.0.1:7000 > CLUSTER ADDSLOTS 0 1 2 3 4 ……5000 表示把槽0至槽5000指派给节点7000负责。

17.2.1 记录节点的槽指派信息

clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽。

struct clusterNode{unsigned char slots[16384/8];int numslots;};

slots属性是一个二进制数组,这个数组长度是16384/8=2048个字节,共包含16384个二进制位

Redis以0为起始索引,16383为终止索引,对slots数组中的16384个二进制位进行编号,并根据索引i上的二进制位的值来判断节点是否负责处理槽i。如果该位为1,表示负责处理

因为取出和设置slots数组中的任意一个二进制位的值的复杂度仅为O(1) ,所以检查节点是否负责处理某个槽,或是把某个槽指派给节点负责,复杂度都是O(1)

numslots属性记录节点负责处理的槽的数量,也就是slots数组中值为1的二进制位的数量

17.2.2 传播节点的槽指派信息

一个节点除了会将自己负责处理的槽记录在clusterNode结构的slots属性和numslots属性之后,它还会将自己的slots数组通过消息发送给集群里的其他节点,以此来告知其他节点自己目前正在负责处理哪些槽。

当节点A通过消息从节点B那里接收到节点B的slots数组时,节点A会在自己的clusterState.nodes字典中查找节点B对应的clusterNode结构,并对结构中的slots数组进行保存或更新。

这样,集群里的每个节点都会将自己的slots数组通过消息发送给集群中的其他节点,并且每个接收到slots数组的节点都会将数组保存到相应节点的clusterNode结构里面。因此集群里的每个节点都会知道数据库的16384个槽分别被指派给了集群里的哪些节点

17.2.3 记录集群所有槽的指派信息

clusterState结构的slots数组记录了集群里所有16384个槽的指派信息

typedef struct clusterstate(clusterNode *slots[16384];
}clusterstate;

slots数组包含16384个项,每个数组项都是一个指向clusterNode结构的指针:如果指向NULL,表示尚未指派给任何节点;如果指向一个clusterNode结构,表示槽i已经指派给了clusterNode结构所代表的节点。通过这样的记录方式,程序要检查槽i是否已经被指派,又或者取得负责槽i的节点,只需要访问clusterState.slots[i]的值即可,这个复杂度仅为O(1)

虽然clusterState.slots数组记录了集群中所有槽的指派信息,但是使用clusterNode结构的slots数组来记录单个节点的槽指派信息仍然是有必要的:因为程序需要将某个节点的槽指派信息通过消息发送给其他节点时,程序只需要将相应节点的clusterNode.slots数组整个发送出去就可以了;如果单独使用clusterState.slots数组的话,每次将节点A的槽指派信息传播给其他节点后,需要先遍历整个数组,记录节点A负责处理哪些槽,才能发送,效率低。

clusterState.slots数组记录了集群中所有槽的指派信息,clusterNode结构的slots数组记录clusterNode结构所代表的节点的槽指派信息。

17.2.4 CLUSTER ADDSLOTS命令的实现

CLUSTER ADDSLOTS 命令接受一个或多个槽作为参数,并将所有输入的槽指派给接收该命令的节点负责

def CLUSTER ADDSLOTS(*allinput slots):
#遍历所有输入槽,检查它们是否都是未指派槽
for i in all input slots:
#如果有哪怕一个槽已经被指派给了某个节点
#那么向客户端返回错误,并终止命令执行if clusterState.slots[i]!=NULL:reply error()return
#如果所有输入槽都是未指派槽
#那么再次遍历所有输入槽,将这些槽指派给当前节点
for i in all input slots:
#设置clusterstate结构的slots数组
#将slots[i]的指针指向代表当前节点的clusterNode结构clusterState.slotsi]=clusterState.myself
#访问代表当前节点的clusterNode结构的slots数组
#将数组在索引i上的二进制位设置为1setSlotBit(clusterstate.myself.slots,i)

命令执行完后,节点会通过发送消息告知集群中的其他节点,自己目前正在负责处理哪些槽

http://www.lryc.cn/news/495651.html

相关文章:

  • 汽车控制软件下载移动管家手机控车一键启动app
  • 推荐几个可以免费下载网站模板的资源站
  • H3C OSPF实验
  • Vue框架开发一个简单的购物车(Vue.js)
  • Windows Terminal Solarized Dark 配色方案调整
  • PyTorch张量运算与自动微分
  • 【从零开始的LeetCode-算法】3264. K 次乘运算后的最终数组 I
  • 【Linux】gdb / cgdb 调试 + 进度条
  • Jenkins Nginx Vue项目自动化部署
  • 视频汇聚平台Liveweb国标GB28181视频平台监控中心设计
  • 文件比较和文件流
  • 【2024最新】基于Springboot+Vue的就业信息管理系统Lw+PPT
  • PySide6 QSS(Qt Style Sheets) Reference: PySide6 QSS参考指南
  • 【笔记】成为雍正
  • Codeforces Round 913 (Div. 3)
  • 斐波那契数
  • Redis高阶集群搭建+集群读写
  • Vision Transformer(vit)的主干
  • 手撸了一个文件传输工具
  • Java程序调kubernetes(k8s1.30.7)core API简单示例,并解决403权限验证问题,即何进行进行权限授权以及验证
  • java八股-Redis Stream和RocketMQ实现的解决方案
  • 第29天 MCU入门
  • 【Python网络爬虫笔记】6- 网络爬虫中的Requests库
  • Linux网络_网络协议_网络传输_网络字节序
  • 浅谈网络 | 应用层之流媒体与P2P协议
  • css vue vxe-text-ellipsis table 实现多行文本超出隐藏省略
  • 基于hexo框架的博客搭建流程
  • 数据结构-简单排序
  • 三十一:HTTP多种重定向跳转方式的差异
  • 利用Python爬虫精准获取淘宝商品详情的深度解析