前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配

《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配

作者头像
用户1327360
发布2018-03-07 16:13:00
1K0
发布2018-03-07 16:13:00
举报
文章被收录于专栏:决胜机器学习决胜机器学习

《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配

(原创内容,转载请注明来源,谢谢)

一、概述

redis集群是redis的分布式数据库的解决方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移的功能。

二、集群的节点

1、节点组成

一个redis集群由多个节点组成,每个节点是一个运行在集群模式下的redis服务器。集群还没建立好时,每个节点可以看成是一个独立的集群,将各个节点联系起来,就会形成一个真正有效的集群。

集群的命令是,clustermeet <ip> <port>,向一个节点node发送此命令发送给另一节点,可以让节点node与发送的ip:port进行一次握手,成功后node将记录该ip与port,添加到集群中。

clusternode命令,可以查看当前节点的集群。

2、启动节点

启动节点的时候,服务器会根据redis配置文件中的cluster-enabled选项,来决定该服务器是否开启集群模式。

由于节点也是一个redis服务器,因此其会使用redis服务器的各种特性,包括用文件事件来处理命令和回复、执行serverCron定时函数、键值对保存的方式、rdb与aof持久化、发布订阅、复制、lua脚本环境与执行lua脚本过程、用redisServer保存服务器状态、redisClient保存客户端状态等。

但是,集群的节点也有一些特殊性,包括特有的定时函数clusterCron(用于节点的其他数据发送gossip消息,检查节点是否断线,检查是否需要对下线节点进行故障转移等),另外还有集群节点特殊的数据结构,保存在cluster.h文件中,包括clusterNode、clusterLink、clusterState等。

三、集群节点数据结构

1、clusterNode结构

该结构保存节点的当前状态,包括名字、ip地址、创建时间、配置纪元、端口号等。集群里的每个节点,都会建立一个clusterNode来记录自身状态,并为集群中的其他节点(包括主从节点)建立相应的clusterNode结构。

主要信息如下:

代码语言:javascript
复制
         struct clusterNode{
         mstime_t ctime;//节点创建的时间
         char name[REDIS_CLUSTER_NAMELEN];//节点名称,由40位16进制字符组成
         int flags;//节点标识,标记节点主从、是否下线等状态
         uint64_tconfigEpoch;//节点创建纪元,用于故障转移
         char ip[REDIS_IP_STR_LEN];//节点ip地址
         int port;//节点端口号
         clusterLink *link;//保存节点所需的有关信息
         //….其他内容省略
}

2、clusterLink结构

该结构是保存在clusterNode中的一个属性,其用于保存节点所需有关信息,如套接字描述符、输入缓冲区和输出区缓冲等。

代码语言:javascript
复制
         typedef struct clusterLink{
         mstime_t ctime;//节点连接的创建时间
         int fd;//套接字描述符
         sds sndbuf;//输出缓冲区,保存等待发送给其他节点的信息
         sds rcvbuf;//输入缓冲区,保存从其他节点接收到的的信息
         struct clusterNode*node;//与这个连接相关的节点,没有就是null
}

redisClient和clusterLink结构都有套接字和相应的输入和输出缓冲区,但是区别在于redisClient是用于连接客户端,clusterLink用于连接其他节点。

3、clusterState结构

每个节点都保存一个这个结构,记录当前节点视角下,集群目前所处的状态,例如集群是否在线、当前有几个节点、当前配置纪元等。

代码语言:javascript
复制
         typedef struct clusterState{
         clusterNode  *myself;//指向当前节点的指针
         uint64_tcurrentEpoch;//集群当前配置纪元,用于故障转移
         int state;//集群当前状态,是否在线
         int size;//集群中至少处理一个槽的节点数量
         dict *nodes;//集群节点名单,包括myself节点,键是节点名称,值是对应的cluster Node结构
         //….其他信息
}clusterState;

4、节点结构

三个节点组成的集群,如下图所示:

其中,每个节点都会有一个这样的结构,区别在于myself指针都是指向各自节点自己。

4、cluster meet实现

cluster meet <ip> <port>,对节点a发节点b的ip,则将节点b加入到节点a的集群中,同时节点b也会将jieda加入到集群中。流程如下:

1)节点a为节点b创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

2)节点a根据ip和端口号,给节点b发送一条meet消息。

3)b会为a创建一个clusterNode,也添加到b自己的clusterState.nodes。

4)b向a回复pong信息。

5)a收到后,会再给b发送一个ping信息。

6)b接收到a的ping信息后,握手完成。

握手流程如下:

7)之后,节点a会将节点b的信息,以gossip协议传播给a当前集群中的其他节点,这样其他节点也会与b进行上述操作。

四、槽(slot)指派

1、概述

redis集群通过分片的方式,来保存键值对。集群的整个数据库被分为16384个槽,数据库中的每个键,都属于16384个槽中的一个,集群每个节点可以处理0~16384个槽。

当集群中,每一个槽都有节点在处理时,则这个集群是上线(ok)的状态;任意一个槽没有节点处理,则该集群下线(fail)。

采用命令cluster info,可以查看当前集群的状态,ok是上线,fail是下线。

通过向节点发送clusteraddslots <slot1> [slot2 slot3 ….]命令,可以将槽指派给节点。槽是用数字从0~16383进行编号的。

在哪个节点输入clusteraddslots,则对该节点指派槽。

2、记录节点指派信息

节点指派的槽的信息,记录在clusterNode结构体中:

代码语言:javascript
复制
         struct clusterNode{
         //….其他信息
         unsigned char slots[16384/8];//二进制数组
         int numslots;//节点处理的槽数量
         };

slots是一个二进制位数组,长度是2048个字节,共包含16384个二进制位。每一个下标代表8个槽,用二进制位表示。如果节点负责某个槽,则数组下标对应的二进制位的相应位置的值是1,否则是0。

例如,下图中的数组,表示节点负责的槽是1、3、5、8、9、10这几个。

使用二进制的方式,目的是便于获取、修改节点负责的槽,因为时间复杂度都是O(1)。

3、传播节点槽指派信息

节点被分配了槽,不仅会记录在节点自身的clusterNode结构体,还会将信息传播给集群的其他节点。

节点a收到节点b的槽分配信息,会从自身记录节点b信息clusterNode的结构中,相应的属性slots与numslots记录槽的位置与槽的数量。

4、记录集群所有槽指派信息

指派信息记录在clusterState结构体中:

代码语言:javascript
复制
typedef stuct clusterState{
//….其他信息
clusterNode *slots[16384];
}clusterState;

这个结构体中,数组每个下标表示一个槽,下标的值都是指针,指向负责该槽的节点。如果某个数组下标是null,表示目前没有节点负责该槽。

redis的设计非常巧妙,该slots属性可以快速找到每个槽对于的负责的节点,而节点内部clusterNode结构的slots,可以快速查找、改变某个节点负责的槽,且获取某个节点负责的全部槽的速度比从clusterState的slots中快得多。

5、槽指派的实现

槽指派之前,会先检查槽是否已经有节点负责,如果一个或以上的槽已经有节点负责,则停止指派,并且报错。

如果所有槽都没有节点负责,则修改clusterState的slots数组,将每个槽下标的值指向该节点的clusterNode结构;并修改该clusterNode的slots数组,将槽对应的二进制位置设置成1。

例如,执行命令clusteraddslots 1 2,节点变化如下:

完成上述命令写入后,节点会发消息通知集群的其他节点。

—written by linhxx 2017.09.15

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 决胜机器学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档