Redis集群是Redis提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。
节点
一个Redis集群通常由多个节点组成,刚开始的时候,每个节点都是相互独立运行的,一个节点,就是一个单独运行的Redis服务器,他们都处于一个只包含自己的集群当中,要建立一个真正可工作的集群,我们必须将各个节点连接起来,构成一个包含多个节点的集群。
打开集群:
cluster-enabled选项设置为yes
假设有三个独立的节点8000,8001,8002(暂时使用端口号区分节点)
一开始redis.conf的cluster-enabled选项为no
1.修改为:cluster-enabled yes
2.
启动8000端口的redis-server:
[root@localhost 8000]# ./bin/redis-server redis.conf
8000文件夹的结构如下,这个文件夹包含了整个redis程序组件
bin目录:
redis.conf内容:
port 8000
cluster-enabled yes
3.
使用客户端连接服务器:
[root@localhost 8000]# ./bin/redis-cli -c -p 8000
127.0.0.1:8000>
4.
输入cluster nodes
127.0.0.1:8000> cluster nodes
0bb7aac0ce20c93923884be95f01bd9856c7cf19 :8000@18000 myself,master - 0 0 0 connected
可以看到此时集群只有8000端口一个节点.
将其他节点加入集群:
[root@localhost 8001]# ./bin/redis-server redis.conf
[root@localhost 8001]# ps -x|grep redis
1284 ? Ssl 0:00 ./bin/redis-server *:8000 [cluster]
1292 ? Ssl 0:00 ./bin/redis-server *:8001 [cluster]
1297 pts/0 S+ 0:00 grep --color=auto redis
[root@localhost 8001]#
此时查询进程会发现有两个redis-server在运行
通过cluster meet
clusterNode的数据结构
使用cluster meet命令连接节点
实际操作,cluster meet执行之前节点的状态
此时可以看到8001节点显示8001节点自己为myself,而且多出来了8000的节点信息
可以发现节点列表中多出了8001的节点信息,通过刚才的握手流程我们可以得知,不管是连接8000还是8001,8000与8001节点握手的过程会让两个节点都在自己的集群信息中存储其他节点的连接信息
这时,就代表集群中不止有8000一个节点了,8000,8001已经组成了一个Redis集群,可以使用
槽指派
Redis通过分片的方式保存数据库中的键值对,集群的整个数据库被分为16384个槽,范围 0-16383,数据库的每个键都属于其中的一个槽,集群中的每个节点可以处理0或16384个槽。
上一节,8000,8001节点已经组成了一个简单的集群,但是集群还没有上线,这是因为16384个槽还没有处理任何槽。
指定槽:
127.0.0.1:8000> cluster addslots 0 1 2 3 [slot....]
通过cluster info命令查询节点槽分配信息
分配前,此时8000节点已经分配了10001个槽,8001还未分配,cluster_state的值为fail,表示集群还未进入上线状态。
127.0.0.1:8000> cluster info #查询集群信息
cluster_state:fail #集群未上线
cluster_slots_assigned:10001 #分配了10001个槽
cluster_slots_ok:10001
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:2
cluster_size:1
cluster_current_epoch:1
cluster_my_epoch:0
cluster_stats_messages_ping_sent:2872
cluster_stats_messages_pong_sent:2868
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:5741
cluster_stats_messages_ping_received:2868
cluster_stats_messages_pong_received:2873
cluster_stats_messages_received:5741
此时给8001节点分配剩下的10001到16383个槽:
127.0.0.1:8000> cluster info #查询集群信息
cluster_state:ok #集群已上线
cluster_slots_assigned:16384 #16384个槽全部已经分配
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:2
cluster_size:2
cluster_current_epoch:1
cluster_my_epoch:0
cluster_stats_messages_ping_sent:3404
cluster_stats_messages_pong_sent:3399
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:6804
cluster_stats_messages_ping_received:3399
cluster_stats_messages_pong_received:3405
cluster_stats_messages_received:6804
cluster_state的值为ok表明在对数据库中的16384个槽都进行指派后,集群就会进入上线状态,这时客户端就可以向集群发送数据命令了。
向集群中执行命令:
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽
1.如果数据库所在的槽正好就指派给了当前节点,那么节点直接执行这个命令。
2.如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端(redirect)正确的节点,并再次发送之前想要执行的命令。
因为键date所在的槽2022正是由节点8000负责处理的。
但是,如果执行以下命令,那么客户端会被转向至节点8001,然后再执行命令:
这是因为键aaa所在的槽6257是由节点8001负责处理的,而不是由最初接收命令的节点8000负责处理。
整个流程可以这样描述:
那么,问题来了,Redis如何计算键所存放的槽呢?
计算键属于哪个槽:
节点使用以下算法来计算给定键key属于哪个槽:
CRC16(KEY) & 16383
其中CRC16(key)用于计算key的CRC-16校验和,而&16383语句用于计算出一个介于0至16383之间的整数作为key的槽号。
记录节点的槽指派信息:
clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽
clusterNode{
// ....
unsigned char slots[16384/8]
}
slots属性是一个二进制位数组,数组的长度为16384/8=2048个字节,共16384个二进制位
举个例子:
使用cluster keyslot
MOVED错误:
MOVED错误的格式为MOVED 10086 127.0.0.1:8002
表示槽10086正由IP地址为127.0.0.1,端口号为8002的节点负责。
当客户端接受到节点返回的MOVED错误之后,客户端会根据MOVED错误中携带的IP地址和端口号,转向至负责处理槽slot的节点,并向节点重新发送之前的命令,
节点8000向客户端返回MOVED错误
客户端接收到MOVED错误并转向8001节点,重新发送命令的过程
复制与故障转移
Redis集群的节点分为主节点和从节点,主节点负责处理槽,从节点负责复制某个主节点的数据,当被复制的主节点下线时,代替下线的主节点继续处理命令请求。
其中单圆形表示从节点,双圆形表示主节点
比如集群中包含7000,7001,7002,7003四个主节点,现在上线了两个从节点,分别是7004,7005,我们可以将7004,7005添加到集群中,并将7004,7005设置为7000的从节点。
集群中各个节点的状态
如果此时7000节点故障下线,那么7004,7005就会选举出一个来作为新的主节点,这个新的主节点将代替7000负责处理客户端命令和请求。
例如,7000节点下线,那么7004就会作为新的主节点负责处理槽0-5000。节点7005也由复制7000节点变为复制7004节点。
故障转移之后的状态
如果在故障转移完成之后,如果7000节点重新上线,那么他将成为7004的从节点。
主节点重新上线,被设置为从节点:
故障检测
集群中的每个节点会定期向其他节点发送PING消息来判断对方的当前是否在线,如果对方在发送PING消息的指定时间之内没有回应PONG消息,那发送PING消息的节点就主观地认为对方已经进入疑似下线状态。
举个例子,假如主节点7001,7002都认为7000进入到疑似下线状态,那么主节点7001将会为7000创建下现报告。在一个集群中,半数以上的主节点都认为某个主节点x进入疑似下线状态,
那么这个主节点将会最终被标识为已下线,将主节点标记为下线的节点会向集群中广播一条x已经下线的消息,所有接收到此消息的节点都会被告知x已经处于下线状态,并标记x为已下线状态。
节点7001向集群中广播7000下线的消息
如图所示,主节点7002,7003都认为7000处于疑似下线状态,综合起来,7001,7002,7003都认为7000疑似已经下线,数量已经超过了半数,所以主节点7001会把7000标记为已下线状态,并向集群中广播一条7000下线的消息。
选举新的主节点
例如集群中包含7000,7001,7002,7003三个主节点,集群此时已经进行过一次故障转移操作,配置纪元当前为1,那么当7000下线时,请求投票的从节点接收到2(3/2+1)个或2个以上的票数支持后,那么这个从节点将会成为新的主节点。
总结:
材料整理自:
推荐书籍《Redis设计与实现 第三版》
(免费订阅,永久学习)学习地址:
C/C++Linux服务器开发/后台架构师【公开课学习】
更多C/C++Linux相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
原文链接:https://zhuanlan.zhihu.com/p/444734866