1. 主从模式
最简单的一种集群方式,集群中分为master数据库和slave数据库,master可以提供读写服务,slave只能提供读服务,所以一单master关掉之后,集群就变成了了只读服务了
2. 哨兵(sentinel)模式
由于主从模式存在一大弊端,主节点挂掉之后无法对外提供写服务,哨兵模式出现了。哨兵模式由哨兵集群和redis集群组成,特点是:
- 哨兵集群只负责监控redis集群,不提供读写服务
- 当master挂掉时,sentinel会选举一个新的master,此时服务会停顿
- 客户端只需要连接sentinel即可,redis集群出现问题,发生重新选举不需要切换数据源
- 只有master提供写服务,slave是只读的,所以master压力很大
- 数据量过大时,master就扛不住了
3. cluster模式
当数据量很大时,就需要对数据进行分布式存储,sentinel只能配置一个master,能力有限。claster(集群模式)是Redis提供的分布式数据方案,集群通过分配进行数据共享,并且提供复制和故障转移功能。涉及到概念节点
、槽指派
、命令执行
、重新分片
、转向
、故障转移
、消息
…
特点
- 节点相互独立,无中心结构
- 每个主节点可以设置多个从节点
- 节点互相连接
- 故障自动转移
- 可以大量水平拓展,官方推荐不超过1000个节点
3.1 节点
几个Redis集群由多个节点组成,最开始节点相互独立,需要我们将它们连接起来,连接命令:
CLUSTER MEET <IP> <PORT>
通过此命令,与目标节点握手,握手成功后,当前节点会将目标节点添加到当前节点集群中,如何查看集群状态:
CLUSTER NODES
启动集群模式配置:cluster-enabled=yes
3.1.1 集群的数据结构
clusterNode结构保存了一个节点的当前状态,如:创建时间、名字、配置纪元、节点ip、port等。
struct clusterNode{
//创建节点时间
mstime_t ctime;
//节点名字,40个16进制字符组成
char name[REDIS_CLUSTER_NAMELEN];
//节点标识
//使用不同的标识值记录节点的角色(比如主节点或者从节点)
//以及节点目前所处的状态(在线或者下线)
int flags;
//节点当前配置纪元,用于实现故障转移
uint64_t configEpoch;
//节点的ip地址
char ip[REDIS_IP_STR_LEN];
//节点端口
int port;
//保存连接节点所需的有关信息
clusterLink *link;
//...
}
其中link
属性是一个clusterLink结构:
typedef struct clusterLink{
//连接的创建时间
mstime_t ctime;
//tcp套字节描述符
int fd;
//输出缓冲区,保存发送给其他节点的消息
sds sndbuf;
//输入缓冲区,保存从其他节点接收的消息
sds rcvbuf;
//与这个链接相关联的节点,没有就为null
struct clusterNode *node;
}
最后每个节点都保存一个clusterState的结构,记录了当前节点视角下,集群目前所处的状态:
typedef struct clusterState{
//指向当前节点的指针
clusterNode *myself;
//集群当前配置单元,用于实现故障转移
uint64_t currentEpoch;
//集群当前状态:上线还是下线
int state;
//集群中至少处理着一个槽的节点的数量
int size;
//集群节点名单(包括myself节点)
//字典的键为节点的名字,值为节点对应的clusterNode结构
dict *nodes;
//...
}
以7001、7002、7003三个节点为例,站在7000的视角:
- currentEpoch为0表示集群配置纪元为0
- size为0,表示当前集群没有任何节点在处理槽,因此state属性为
REDIS_CLUSTER_FAIL
,表示集群处于下线状态- nodes字典记录了集群目前包含的三个节点,分别由三个clusterNode表示,其中myself指向7000的clusterNode结构,字典的另外两个指针指向7001、7002的clusterNode结构,这两个节点是7000已知咋的在集群中的其它节点
- flags都是
REDIS_NODE_MASTER
,说明都是主节点
站在7001、7002的视角如同
3.1.2 CLUSTER MEET命令的实现
客户端向节点A发送CLUSTER MEET
命令,可以让节点A将节点B添加到节点A所在的集群里,握手过程如下
- A为B创建一个clusterNode结构,并将其添加到自己的clusterState.nodes字典里
- A根据给定的ip和端口号向B发送一条MEET消息
- B如果收到A的消息,会为A创建一个clusterNode结构,并将其添加到自己的clusterState.nodes字典里
- B向A返回一条PONG消息
- 如果A收到B的PONG消息,说明一切顺利,A就知道B成功接收到自己的MEET消息
- A向B返回一条PING消息
- 握手完成(和TCP三次握手很像)
之后A会将B的信息传播给集群中其它节点,让其它节点与B握手,最终B被所有节点认识
3.2 槽指派
Redis通过分片方式保存数据:整个数据库被分为16384个槽(Slot),每个键都属于这些槽其中的一个,每个节点可以处理0-16384个槽
当这些槽全部都有节点处理时,集群处于上线(ok)状态;反之处于(fail),上面已经将7001、7002、7003加到同一个集群里面,但是此时集群处于fail状态,因为三个节点都没有指派槽,槽指派命令:
CLUSTER ADDSLOTS <slot> [slot ...]
举个例子,如何将0到5000指派给7001:
127.0.0.1:7000 > CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
ok
同理可以将剩下的槽指派给剩余节点,尽量保持平均