概述
本篇博客主要是用来说明redis集群的工作原理。
Q1.为什么要有redis集群,redis集群解决了哪些问题?
A1:redis集群是分布式数据库的解决方案,我个人理解其主要解决了写库的性能瓶颈,也就是横向扩展了写的能力。并且提供了复制功能和故障转移功能。
下面我们就一一来研究他们
一、集群的节点的组成
二、槽指派
三、在集群中命令如何执行的?
四、重新分片
五、master节点宕机后如何实现故障转移
redis集群中的节点
redis集群是由各个节点组成的,开始时各个节点都是相互独立的。要使他们组合成一个集群,我们需要把各个相互独立的节点连接起来。
现在我假设有三台服务器 node1 127.0.0.1:7000;node2:127.0.0.01:7001 node3:127.0.0.1:7002
登录node1
执行CLUSTER NODES 命令
可以发现目前集群中只有node1这一个节点
向node1 发送 命令 CLUSTER MEET 127.0.0.1 7001 这样就让node2加入了集群
也就是说cluster meet命令相当于扩展了集群中的节点。
让node2加入集群也执行同样的操作。
怎样让一个redis服务器以集群模式启动呢?redis服务器会检查配置文件 中cluster-enabled配置项,如果为true就以集群模式启动,如果为false就以单机(stand alone)模式启动。
下面了解一些集群中的数据结构,将会对我们了解redis集群的运行原理有很大帮助。
clusterNode结构。clusterNode结构保存了集群中一个节点的当前状态,比如说 节点的创建时间,节点的配置纪元,节点的ip和端口号等
集群中的每个节点都会为自己创建一个clusterNode结构记录自己的状态,同时也会为集群中其他的节点创建clusterNode结构,以此来记录其他节点的状态。
struct clusterNode{
//创建节点的时间
mstime time;
//节点的名字
char name;
//节点标识
//使用不同的标识值记录节点的角色(比如是从节点还是主节点)以
//以及节点所处的状态(比如在线或者下线)
int flags;
//节点的纪元,用于实现故障转移
int configEpoch;
char ip;
int port;
clusterLink *link;
}
clusterNode结构的link属性是一个clusterLink结构,该结构保存了连接节点所需要的有关信息,比如套接字描述符,输入输出缓冲区:
clusterLink{
//连接的时间
mstime time;
//Tcp套接字
int fd;
//输出缓冲区,保存着等待发送给其他节点的消息
sds sndbuf;
//输入缓冲区,保存着从其他节点收到的消息
sds rcvbuf;
//与这个链接相关联的节点
clusterNode ndoe,
// 节点处理的槽
char slots[16384/8];
int numslots;
}
另外集群中每个节点中还保存着一个clusterState结构,这个结构记录了在当前节点的视角下,集群所处的状态。比如说集群是在线还是下线,集群中有多少个节点,集群当前的配置纪元
clusterState{
//指向当前节点的指针
clusterNode myself;
//集群当前的配置纪元
int currentEpoch;
//集群当前的状态,是在线还是下线
int state;
//集群中至少处理着一个槽的节点的数量
//集群节点名单(包括myself节点自己)字典的key为节点的名字,值是cluterNode结构
dict nodes,
//slots数组记录了集群中所有16384个槽的指派信息
//slots数组包含16384个项,每个数组项都是指向clusterNode指针结构的结构体,如果slots[i]指向null,那么槽i尚未分配给任何节点,如果slots[i]指向一个clusterNode结构,那么表示槽i已经分配给了clusterNode所代表的节点。
clusterNode slots[16384]
// 如果import_slots_from[i]不是空,则表示当前节点正在从clusterNode所代表的节点导入槽i
向目标节点发送cluster setslot importing <source_id>
可以将目标节点clusterState.importing_slots_from[i]设置为source_id
所代表的目标节点。
clusterNode importing_slots_from[16384];
//migrating_slots_to 数组记录了当前节点正在迁移至其他节点的槽
如果migrating_slots_to[i]不是空,而是一个clusterNode结构,则表示当前节点正在将槽i迁移至clusterNode所代表的节点。
clusterNode migrating_slots_to[16384];
}
二、槽的指派过程
redis集群通过分片的方式来保存数据中的键值对:集群的整个数据库被分为16384个槽,数据库中的每个键都属于16384个槽中的其中一个,集群中的每个节点都可以处理0个或最多16384个槽
当数据库中的16384个槽都有节点在处理时,集群处于上线状态,相反的,如果数据库中有任何一个槽没有得到处理,那么集群就处于下线状态。
这句话就是说我不把16384个节点都分配出去,集群就是下线状态。
利用CLUSTER ADDSLOTS命令,我们可以将一个或者多个槽指派给节点负责:
CLUSTER ADDSLOTS[slot]
例如 CLUSTER ADDSLOTS 0 1 2 3 …5000
比如说node1处理 0-5000这5001个槽,他会把这个信息发送给集群中的其他节点,以此来告知其他节点自己目前负责的槽。就是把slots数组发送给其他节点,其他节点会更新发送者的信息。
三、命令的执行过程
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己。
第一步:如果键所在的槽正好指派给了当前节点,那么该节点就直接执行这个命令。
第二步:如果键所在的槽并没有指派给当前节点,那么节点会给客户端返回一个MOVED错误,指引客户端redirect至正确的节点,并再次发送之前想要执行的命令。
计算节点属于哪个槽的函数
def slot_number(key)
return CRC16(key)&16383
利用CLUSTER KEYSLOT命令可以判断一个key属于哪个槽
怎样判断槽是否由当前节点负责处理
1)如果clusterState.slots[i]等于clusterState.myself,那么说明槽i由当前节点负责,节点可以执行客户端发送的命令。
2)如果clusterState.slots[i]不等于clusterState.myself,那么说明槽i并非由当前节点负责,节点会根据clusterState.slots[i]所指向的clusterNode结构中的ip和port,向客户端发送MOVED错误,指引客户端转向槽i被持有的节点
最后
以上就是还单身哑铃为你收集整理的redis集群模式的工作原理(一)的全部内容,希望文章能够帮你解决redis集群模式的工作原理(一)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复