一、计算键所属槽(CLUSTER KEYLOST命令)
使用的算法(CRC16语句)
- 节点使用以下伪代码算法来计算给定键key属于哪个槽:
def slot_number(key):
return CRC16(key) & 16383
- 其中CRC16(key)语句用于计算键key的CRC-16校验和,而&16383语句则用于计算出 一个介于0至16383之间的整数作为键key的槽号
CLUSTER KEYLOST命令
- 使用“CLUSTER KEYSLOT <key>命令”可以查看一个给定键属于哪个槽:
- 例如:

- CLUSTER KEYSLOT命令就是通过调用上面给出的槽分配算法来实现的,以下是该命令 的伪代码实现:
def CLUSTER_KEYSLOT(key):
# 计算槽号
slot = slot_number(key)
# 将槽号返回给客户端
reply_client(slot)
二、判断槽所属节点(struct clusterState.slots数组)
- 当节点计算出键所属的槽i之后,节点就会检查自己在clusterState.slots数组中的项i,判断键所在的槽是否由自己负责:
- 如果clusterState.slots[i]等于clusterState.myself,那么说明槽i由当前节点负责,节点可以执行客户端发送的命令
- 如果clusterState.slots[i]不等于clusterState.my self,那么说明槽i并非由当前节点负责,节点会根据clusterState.slots[i]指向的clusterNode结构所记录的节点IP和端口号,向客户端返回MOVED错误,指引客户端转向至正在处理槽i的节点
演示案例
- 举个例子,假设下图为节点7000的clusterState结构:
- 当客户端向节点7000发送命令SET date"2013-12-31"的时候,节点首先计算出键date属于槽2022,然后检查得出clusterState.slots[2022]等于clusterState.myself,这说明槽2022正是由节点7000负责,于是节点7000直接执行这个SET命令,并将结果返回给发送命令的客户端
- 当客户端向节点7000发送命令SET msg"happy new year!"的时候,节点首先计算出键 msg属于槽6257,然后检查clusterState.slots[6257]是否等于clusterState.my self,结果发现两者并不相等:这说明槽6257并非由节点7000负责处理,于是节点7000访问 clusterState.slots[6257]所指向的clusterNode结构,并根据结构中记录的IP地址127.0.0.1和端口号7001,向客户端返回错误MOVED 62 127.0.0.1:7001,指引节点转向至正在负责处理槽6257的节点7001

三、MOVED错误
- 何时出现?当节点发现键所在的槽并非由自己负责处理的时候,节点就会向客户端返回一个 MOVED错误,指引客户端转向至正在负责槽的节点
- MOVED错误的格式为:
- 其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地址和端口号
MOVED <slot> <ip>:<port>
- 当客户端接收到节点返回的MOVED错误时,客户端会根据MOVED错误中提供的IP地址和端口号,转向至负责处理槽slot的节点,并向该节点重新发送之前想要执行的命令
- 一个集群客户端通常会与集群中的多个节点创建套接字连接,而所谓的节点转向实际上就是换一个套接字来发送命令
- 如果客户端尚未与想要转向的节点创建套接字连接,那么客户端会先根据MOVED错误提供的IP地址和端口号来连接节点,然后再进行转向
单机模式/集群模式下MOVED错误的显示
- 集群模式的redis-cli客户端在接收到MOVED错误时,并不会打印出MOVED错误, 而是根据MOVED错误自动进行节点转向,并打印出转向信息,所以我们是看不见节点返回的MOVED错误的:

- 但是,如果我们使用单机(stand alone)模式的redis-cli客户端,再次向节点7000发送相同的命令,那么MOVED错误就会被客户端打印出来:

- 这是因为单机模式的redis-cli客户端不清楚MOVED错误的作用,所以它只会直接将 MOVED错误直接打印出来,而不会进行自动转向
演示案例
- 以前 面的客户端从节点7000转向至7001的情况作为例子:

- 下图展示了客户端向节点7000发送SET命令,并获得MOVED错误的过程

- 而下图则展示了客户端根据MOVED错误,转向至节点7001,并重新发送SET命令的 过程
