一个Redis集群通常由多个节点组成,当节点node发送CLUSTER MEET ip Port
进行握手(节点连接的方式),握手成功,则节点将ip和Port所指定的节点添加到当前所在的集群中。
-
集群的数据结构:每个clusterNode结构保存着一个节点的当前状态,节点的创建时间、名字等。每个节点同时还会为集群中的其他节点创建一个相应的clusterNode来记录其他节点的状态。同时每个接待你还保存一个clusterState记录当前节点视角的集群所处状态。
-
Cluster Meet 命令的实现
- 节点A收到Cluster命令后,为B创建一个clusterNode结构,并添加到自己的nodes字典中
- 节点A根据给定的IP地址和端口向节点B发送一条MEET消息
- B收到A的消息后为A在自己的nodes字典中创建A的clusterNode,并返回pong
- 节点A成功收到消息后,表示B接受了A的消息,并向A返回一条PING消息
- B收到A的PING消息表示握手成功。
槽
-
Redis集群通过分片的方式来保存数据库中的键值对。集群被整个数据库分成16384个槽,当每个槽都有节点处理时,集群处于上线状态。任何一个槽没有被处理,集群就处于下线状态。
-
可以通过
CLUSTER ADDSLOTS
命令来添加槽指派信息,如果在slots数组的索引槽上的二进制值为1,表示节点负责处理这个槽i,否则表示不负责处理。 -
槽的信息不仅保存在clusterNode.slots数组中,还保存在clusterState.slots中。如果是保存在节点的 clusterNode.slots数组中,那么为了知道某个槽是否被指派,程序就需要遍历所有node字典中的槽,直到找到槽i,这个时间复杂度需要O(N)。而根据clusterState.slots,能直接检查,时间复杂度为o(1)。clusterNode.slots数组只记录当前节点的槽指派信息,而clusterState.slots记录了集群中所有槽的指派信息。
在集群中执行命令
当全部槽被指派后,集群会进入上线状态,此时客户端能向集群发送数据命令。 -
客户端发送与数据库键有关的命令时,首先计算出数据库键属于哪些槽,检查槽是不是指派给自己。如果指派给当前节点,就执行这个命令。如果不是,返回一个MOVED错误,提示转向正确的节点,并再次发送命令。
MOVED错误:节点发现键不是自己负责处理,就向客户端返回一个MOVED错误,指引客户端转向正在负责槽的节点。
重新分片 -
重新分片指的是:可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。
-
实现原理:
在重新分片后,这一指派消息会发送至整个集群,让所有节点都知道槽slot已经指派给了目标节点。
ASK错误 -
重写分派可能出现的问题:被迁移槽的一部分键值对保存在原节点中,另一部分保存在目标节点里。
-
如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。当客户端接收到ASK错误并转向至正在导人槽的节点时,客户端会先向节点发送一个ASKING命令,然后才重新发送想要执行的命令,如果客户端不发送ASKING命令那么命令会被拒绝执行。
-
MOVED和ASK错误的区别
- MOVED错误代表槽 的负责权已经从一个节点转移,每次收到关于槽 i 的命令请求都能直接发送到MOVED指向的节点。
- ASK错误只是两个节点迁移槽的时候使用的临时措施。
-
复制:集群中的主节点用来处理槽,从节点用来复制某个主节点,并且在主节点下线时能够代替主节点继续处理命令请求,这和Sentinel模式类似。当一个节点成为从节点,主节点会通过消息发送给集群中的其他节点。
-
故障检测:集群中的每个节点会定期向集群中的其他节点发送PING消息,如果在指定时间内没收到PONG消息,就将这个节点标记为疑似下线。然后集群中的各个节点通过互发消息来交换集群中节点的状态信息,如果半数以上负责处理槽的主节点都被某个主节点报告为疑似下线,那么就会被标记为已下线。
-
故障转移:
-
消息:集群中的节点通过发送和接受消息来进行通信,其中所有的消息都被消息头包裹,消息头出来包含消息正文外,还记录消息发送者自身的一些能被消息接受者用到的信息。