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

Redis - 讲清楚集群模式(Redis Cluster)(上)

节点与集群构建

  • 节点组成:Redis集群由多个独立节点组成,通过CLUSTER MEET命令实现节点握手,形成集群。

  • 握手过程

    1. 节点A为节点B创建clusterNode结构,发送MEET消息。

    2. 节点B接收后创建节点A的clusterNode结构,返回PONG消息。

    3. 节点A返回PING消息,完成握手。

之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间之后,节点B会被集群中的所有节点认识。

  • 数据结构

    • clusterNode: 结构的link属性是一个 clusterLink 结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输人缓冲区和输出缓冲区:

    struct clusterNode {// 创建节点的时间mstime_t ctime;// 节点的名字,由40个十六进制字符组成// 例如68eef66df23420a5862208ef5b1a7005b806f2ffchar name[REDIS_CLUSTER_NAMELEN];// 节点标识// 使用各种不同的标识值记录节点的角色(比如主节点或者从节点)// 以及节点目前所处的状态(比如在线或者下线)。int flags;// 节点当前的配置纪元,用于实现故障转移uint64_t configEpoch;// 节点的IP地址char iP[REDIS_IP_STR_LEN];// 节点的端口号int port;// 保存连接节点所需的有关信息clusterLink *link;// 负责的槽位(二进制位数组)unsigned char slots[16384 / 8]; int numslots; //...
    };
    • clusterLink:clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输人缓冲区和输出缓冲区.

    typedef struct clusterLink {// 连接的创建时间mstime_t ctime; // TCP 套接字描述符int fd; // 输出缓冲区(保存待发送给其他节点的消息)sds sndbuf; // 输入缓冲区(保存从其他节点接收的消息)sds rcvbuf; // 关联的节点(NULL 表示未关联)struct clusterNode *node; 
    } clusterLink;
    • clusterState:每个节点都保存着一个 clusterState 结构,这个结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元,诸如此类。

typedef struct clusterState {// 当前节点自身的指针clusterNode *myself; // 集群当前配置纪元(用于故障转移)uint64_t currentEpoch; // 集群状态(在线/下线)int state; // 处理至少一个槽的节点数量int size; // 集群节点字典(键为节点ID,值为 clusterNode 结构)dict *nodes; // 所有槽的指派信息(数组索引为槽号,值为指向 clusterNode 的指针)clusterNode *slots[16384]; // 当前节点正在导入的槽(用于重新分片)clusterNode *importing_slots_from[16384]; // 当前节点正在迁移的槽(用于重新分片)clusterNode *migrating_slots_to[16384]; // 槽与键的映射关系(跳跃表,分值=槽号,成员=键名)zskiplist *slots_to_keys; 
} clusterState;

槽指派与数据分片

  • 槽总数:16384个槽,所有槽被指派后集群上线(cluster_state:ok)。

  • 槽指派命令

CLUSTER ADDSLOTS <slot>将槽指派给节点。

127.0.0.1:7000>CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
OK

查看集群状态

127.0.0.1:7000>CLUSTER NODES
9dfb4c4e016e627d9769e4c9bb0d4fa208e65c26 127.0.0.1:7002 master - 0 1388317426165
  • 槽信息存储(节点视角)clusterNode.slots(二进制位数组,记录自身负责的槽)。

slots属性 unsigned char 占用 1 个字节(8 位)的内存,所有位都用于表示数值,没有符号位。因此,其取值范围为 ‌0 到 255‌,是一个二进制位数组(bitarray),这个数组的长度为16384 / 8=2048个字节,共包含16384个二进制位。

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

  1. 如果slots数组在索引i上的二进制位的值为1,那么表示节点负责处理槽i。

  2. 如果slots数组在索引i上的二进制位的值为0,那么表示节点不负责处理槽i。

展示了一个slots数组示例:这个数组索引0至索引7上的二进制位的值都为1,其余所有二进制位的值都为0,这表示节点负责处理槽0至槽7。

通过位运算检查槽指派,时间复杂度为O(1)

  • 槽信息存储(集群视角):clusterstate结构中的slots数组记录了集群中所有16384个槽的指派信息(指针数组,直接定位槽所属节点)。查找时间复杂度为O(1)

clusterstate.slots数组记录了集群中所有槽的指派信息,而clusterNode.slots数组只记录了clusterNode 结构所代表的节点的槽指派信息,多用于某个节点本身的槽信息发送给其他节点,这是两个slots数组的关键区别所在。

  • 传播节点的槽指派信息:

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

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

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

相关文章:

  • C++ 创龙UDP通讯demo
  • 如何在vue3项目中使用 AbortController取消axios请求
  • UWB技术与5G、物联网结合的应用前景
  • c# 根据圆面积计算最大芯片数量
  • Java虚拟机面试题:类加载机制
  • 【grafana原生告警中心配置飞书机器人告警】
  • 微信小程序-自定义toast
  • 最近准备写个Playbook,详细点的指导手册,作为后续的销售培训文件,也趁着这个机会整理下产品思路,尤其是对于UALink,UEC新的联盟规范的测试用例
  • 深入剖析 FastAdmin 权限验证体系:与 UserRule 表的紧密关联及最佳实践
  • 智能合约安全审计平台——可视化智能合约漏洞扫描
  • *差分自回归移动平均模型(ARIMA)
  • # 从零开发小红书风格Flutter应用:图片上传功能实现踩坑记录
  • Spark-SQL2
  • OpenCV中的轮廓近似方法详解
  • MCP Server 开发实战 | 大模型无缝对接 Grafana
  • 49、发起流式请求获取回答
  • Jarpress 开源项目重构公告
  • Java设计模式之观察者模式:从入门到架构级实践
  • 安卓基础(SQLite)
  • 设计模式之状态模式:优雅管理对象行为变化
  • 比尔·盖茨:未来20年通过盖茨基金会捐出几乎全部财富,2045年底基金会停止运营
  • 奥园集团将召开债券持有人会议,拟调整“H20奥园2”本息兑付方案
  • 上海优化营商环境再攻坚,企业和机构有哪些切实感受?
  • 九部门:对机动车特别是货车排放问题的监管将更加严格
  • 俄乌互相空袭、莫斯科机场关闭,外交部:当务之急是避免局势紧张升级
  • 两个灵魂,一支画笔,意大利艺术伴侣的上海灵感之旅