集群由redis服务器节点组成,通过节点间互相meet握手构成集群
节点握手:
两次握手过程如下所示:
收到meet命令的节点A将会与节点B进行握手,以此来确认彼此的存在
节点A将会为节点B创建一个 clusterNode结构,并将该结构添加到自己的clusterState.nodes字典
节点B收到节点A的MEET,B为A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes
之后节点B向节点A返回pong
节点A收到pong,表示节点B收到了A的meet,然后A向B发送ping,
节点B收到A的ping表示,B知道了A已经收到了B的pong,握手完成
槽指派
16384个槽,任意一个槽都需要被节点处理,当存在槽未被指派给节点处理,集群处于下线状态
节点的槽指派信息的记录与传播
redis通过clusterNode结构中的slots和numslot属性来记录槽指派信息
slots是一个二进制位数组,一共16384个二进制位,即slots数组的长度为16384/8=2048
redis从0到16383进行索引编号,即slots[i]上有8个索引,索引处为1说明节点负责处理槽i,为0说明节点不负责处理此处槽i
数组管理槽使得redis处理槽或者查看槽得时间复杂度都为O(1)
numslot属性表示节点管理得槽得数量,即slots中为1的二进制位的个数
节点会通过消息把自己的slots数组发送给别的节点,收到slots数组的节点就会将数组保存到相应节点的clusterNode结构中,因此,集群中每个节点都知道16364个槽的分配情况
clusterState结构中的slots数组记录集群中所有16384个槽的指派信息
如果slots[i]指针指向null,表示槽i尚未指派给人和街店
如果slots[i]指针指向一个clusterNode结构,表示槽i指派给了clusterNode结构所代表的节点
使用clusterState结构中的slots数组的意义:若是想知道槽i是否被指派或者被指派给哪个节点,只使用clusterNode结构中的slots数组的话,就需要O(n),使用clusterState结构中的slots,只需要访问clusterState.slots[i],时间复杂度为O(1)
同样clusterNode节点也是有优势的:当节点需要发送给别的节点自己的槽指派信息的时候,只需要发送clusterNode的slots数组,不然的话还得去clusterState中的slots数组去遍历找自己负责的槽是那些,会低效很多
在集群中执行命令
接受命令的节点会计算出命令要处理的数据库键处于哪个槽,并检查这个槽是否指派给了自己
如果键所在槽指派给自己,直接执行;否则给客户端返回一个MOVED错误,指引客户端转向至正确的点,并再次发送之前想要执行的命令
计算键属于哪个槽的方法:CRC16(KEY) && 16383
而CRC16(key)计算key的校验和,&16383计算出一个介于0至16383的整数作为key的槽号
如果clusterState.slots[i]等于clusterNode.myself,直接执行,否则的话,根据clusterState.slots[i]指向的clusterNode结构中的ip和端口号,向客户端发送MOVED错误。
使用跳表保存键和槽的关系
重新分片
重新分片可以在线进行,即将已经指派给某个节点的槽(包括槽所属的键值对)转为指派给另一个节点
重新分片过程:
让目标节点准备好导入槽slot键值对
让源节点准备迁移槽slot键值对
迁移键值对,(每次最多count,重复直至完成)
将槽slot指派给目标节点
ASK错误
重新分片的过程中,若是客户端发送命令过来,该命令命中还未迁移的键值对时,正常执行返回,若是命中部分已经迁移到了目标节点,向客户端发送一个ASK错误,同时引导客户端导向目标节点
ASK和MOVED区别
MOVED可以认为是责任转移,即后边的客户端继续访问槽i,会直接落到目标节点上去
ASK则只会在接下来的一次请求中落到目标节点