Redis 集群
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。
下面会对集群的节点、槽指派、命令执行、重新分片、转向、故障转移、消息等各个方面进行介绍
一、节点
1. 连接节点
一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作可以使用 CLUSTER MEET命令来完成。该命令的格式如下:
CLUSTER MEET <ip> <port>
向一个节点node发送CLUSTER MEET命令,可以让node节点与ip和port所指定的节点进行握手(handshake),当握手成功时,node节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。
例如:通过向节点7000发送以下命令,我们可以将节点7001添加到节点7000所在的集群里面
127.0.0.1:7000> CLUSTER MEET 127.0.0.1 7001
节点会继续使用redisServer结构来保存服务器的状态,使用redisClient结构来保存客户端的状态,至于那些只有在集群模式下才会用到的数据,节点将他们保存到了cluster.h/clusterNode结构、cluster.h/clusterLink结构,以及cluster.h/clusterState结构里面。
2. 集群数据结构
1)clusterNode结构
clusterNode结构保存了一个节点的当前状态,比如节点的创建时间、节点的名字、节点当前的配置纪元、节点的IP地址和端口号等等。
每个节点都会使用一个clusterNode结构来记录自己的状态,并为集群中所有其他节点(包括主节点和从节点)都创建一个相应的clusterNode结构,以此来记录其他节点的状态。
// 这是对集群节点的描述,是集群运作的基础 typedef struct clusterNode { // 节点创建时间 mstime_t ctime; /* Node object creation time. */ // 节点名称,也叫做节点ID,由40个十六进制字符组成 // 启动后会存储在node.conf中,除非文件删除,否则不会改变 char name[REDIS_CLUSTER_NAMELEN]; // 节点标识 // 使用各种不同的标识值记录节点的角色(比如主节点或者从节点) // 以及节点目前所处的状态(比如在线或者下线) int flags; // 节点当前的配置纪元,用于实现故障转移 uint64_t configEpoch; // 代表节点负责的哈希槽 unsigned char slots[CLUSTER_SLOTS/8]; // 节点负责哈希槽的数量 int numslots; // 节点的复制偏移量 long long repl_offset; // 节点的ip地址 char ip[NET_IP_STR_LEN]; // 节点端口号 int port; // 与节点的网络链接 clusterLink *link; // 报告此节点宕机的节点列表 list *fail_reports; // ... }
2)clusterLink结构
clusterNode结构的 link 属性是一个clusterLink结构,该结构保存了连接节点所需的有关信息,比如套接字描述符,输入缓冲区和输出缓冲区。
1 typedef struct clusterLink { 2 // 连接的创建时间 3 mstime_t ctime; 4 5 // TCP 套接字描述符 6 int fd; 7 8 // 与远程节点的网络链接 9 connection *conn; 10 11 // 输出缓冲区,保存着等待发送给其他节点的消息(message) 12 sds sndbuf; 13 14 // 输入缓冲区,保存着从其他节点接收到的消息 15 sds rcvbuf; 16 17 // 与这个连接相关联的节点,如果没有的话就为NULL 18 struct clusterNode *node; 19 20 } clusterLink;
clusterLink封装了远程节点实例,以及与其的网络链接、接收和发送数据包的信息,基于它两个节点之间就可以保持实时通信了。
需要注意的是:集群总线中所使用的端口并不是我们之前熟悉的6379这样服务于客户端的端口,而是专用的;它不是我们手动设置的,而是由服务于客户端的端口通过偏移计算(+10000)而来。比如,服务于客户端的端口为6379,那么集群总线监听的端口就为16379。
所以,若需要以集群模式部署Redis实例,我们必须保证主机上两个端口都是非占用状态,否则实例会启动失败。
3) clusterState结构
每个节点都保存着一个clusterState 结构,这个结构记录了在当前节点的视角下,集群目前所处的状态,例如集群是在线还是下线,集群包含多少个节点,集群当前的配置纪元,诸如此类:
1 // 这个结构存储的是从当前节点视角,整个集群所处的状态 2 typedef struct clusterState { 3 // 指向当前节点的指针 4 clusterNode *myself; 5 6 // 集群当前的配置纪元,用于实现故障转移 7 uint64_t currentEpoch; 8 9 // 集群当前的状态:是在线还是下线 10 int state; 11 12 // 集群中至少处理着一个槽的节点的数量 13 int size; 14 15 // 集群节点名单(包括myself节点) 16 // 节点字典:name->clusterNode(字典的键为节点的名字,字典的值为节点对应的clusterNode结构) 17 dict *nodes; 18 19 //... 20 }clusterState;
二、槽指派
Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。
当数据库中的16384个槽都有节点在处理时,集群处于上线状态(ok);相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。通过向节点发送CLUSTER ADDSLOTS命令, 我们可以将一个或多个槽指派(assign)给节点负责。例如:执行以下命令可以将槽0至槽5000指派给节点7000负责:
127.0.0.1:7000> CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
参考资料:
《Redis设计与实现》
标签:clusterNode,Redis,CLUSTER,集群,节点,结构 From: https://www.cnblogs.com/hld123/p/18332094