集群
概述
每个Redis服务器启动起来时,都会去检查配置cluster-enabled
。如果是yes,自然就成为集群的节点。如果no,那么就作为一个服务器单机。
对于每一个节点服务器用到的数据,Redis 将他们保存到了clusterNode
,cliusterLikn
,clusterState
三个结构中。
Redis 节点与节点之间,通过命令cluster meet ip port
来互相连接成为一个集群。节点与节点之间通信流程如下:
-
1 节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState. nodes字典里面。
-
2 之后,节点A将根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息( message )。
-
3 如果- -切顺利,节点B将接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState. nodes字典里面。
-
4 之后,节点B将向节点A返回一条PONG消息。
-
5 如果-切顺利,节点A将接收到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功地接收到了自己发送的MEET消息。
-
6 之后,节点A将向节点B返回- -条PING消息。
-
7 如果一切顺利,节点B将接收到节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功地接收到了自己返回的PONG消息,握手完成。
Redis 集群的数据分布之分片
Redis通过分片的方式来保存数据库中的键值对,集群的整个数据库被分成16384个槽,每一个键都会属于到某一个槽中,每个节点处理部分槽。
当16384中的任意一个槽没有被使用时,Redis集群都是下线的。
-
每个节点都会通过一个数组
slots
来记录自己负责哪些槽(这里就使用了位图的数据结构) -
每个节点的
clusterState
结构还会有一个数组(clusterNode *slots[16384]
)来记录所有槽被哪个节点负责。
clusterNode 结构解读
- 结构
clusterLink *link
保存了与其他节点TCP连接的所有信息。(比如:socketfd,缓冲区等)。
clusterState 结构解读
每个节点保存一个,这个结构主要记录的是从当前节点的角度出发,集群目前所处的状态,集群包含多少个节点等信息。
- 结构 nodes : 与我这个节点通信的所有节点的
clusterNode
。
在集群中执行命令
- 计算:CRC16(key) & 16384
- moved:重定向
重新分片
-
1 redis-trib对目标节点发送CLUSTER SETSLOT IMPORTING <source_id>命令,让目标节点准备好从源节点导人( import)属于槽slot的键值对。
-
2 redis-trib对源节点发送CLUSTER SETSLOT MIGRATING <target_ id>命令,让源节点准备好将属于槽slot的键值对迁移( migrate)至目标节点。
-
3 redis-trib向源节点发送CLUSTER GETKEYSINSLOT 命令,获得最多count个属于槽slot的键值对的键名( key name )。
-
4 对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE<target_ ip> <target_ port> <key_ name> 0 命令,将被选中的键原子地从源节点迁移至目标节点。
-
5 重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。每次迁移键的过程如图17-24所示。
-
6 redis-trib向集群中的任意-一个节点发送CLUSTER SETSLOT NODE<target_ id> 命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点。
这其中就有ASK错误(迁移过程中,没有在原来的DB中找到时)
其实我想不通的是:为啥要有一个 ASKING 标识呐,而且还只能够使用一次??有啥子作用嘛?这玩意儿之后还是会发送到原来的DB上去。
复制与故障转移
// 节点状态
struct clusterNode {
// 创建节点的时间
mstime_t ctime; /* Node object creation time. */
// 节点的名字,由 40 个十六进制字符组成
// 例如 68eef66df23420a5862208ef5b1a7005b806f2ff
char name[REDIS_CLUSTER_NAMELEN];
// 节点标识
// 使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
// 以及节点目前所处的状态(比如在线或者下线)。
int flags;
// 由这个节点负责处理的槽
unsigned char slots[REDIS_CLUSTER_SLOTS/8];
// 该节点负责处理的槽数量
int numslots;
// 如果本节点是主节点,
//那么用这个属性记录从节点的数量
int numslaves;
// 指针数组,指向各个从节点
struct clusterNode **slaves;
// 如果这是一个从节点,那么指向主节点
struct clusterNode *slaveof;
}
故障检测
定期发送消息不用说,检测不到标记 flag 呗。
当-个主节点A通过消息得知主节点B认为主节点C进人了疑似下线状态时,主节点A会在自己的clusterState. nodes字典中找到主节点C所对应的clusterNode结构,并将主节点B的下线报告( failure report) 添加到clusterNode结构的fai1_ reports链表里面:
哎,这个其实就是一个链表,保存了所有其他节点认为我这个节点下线的报告。
故障转移
慢查询
Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求,用户可以通过这个功能产生的日志来监视和优化查询速度。
服务器配置有两个和慢查询日志相关的选项:
- slowlog-log-slower-than :规定时间
- slowlog-max-len:规定条数
struct redisServer {
// 保存了所有慢查询日志的链表
list *slowlog; 保存的结构是slowlogEntry
// 下一条慢查询日志的 ID
long long slowlog_entry_id;
// 服务器配置 slowlog-log-slower-than 选项的值
long long slowlog_log_slower_than;
// 服务器配置 slowlog-max-len 选项的值
unsigned long slowlog_max_len;
}
新的日志会被放在链表头部,打印日志通过遍历来实现。
这几天,再过一遍《Linux内核设计与实现》+《深入理解Linux内核》+《深度探索C++对象模型》+《Effective C++》 吧!!!