在前两篇Redis集群架构剖析中,我们创建了一个集群,并且把这个集群由下线状态启动为上线状态。集群上线之后,就表示我们可以向集群的节点发送数据命令了。在开始之前,依旧可以先思考下面的问题:
针对上面的问题,结合前两篇的内容,大致可以猜一下流程可以是,先找到对应的key的槽位在哪?是不是我发送到的节点上面处理的,如果是的话就执行,不是的话可能就转给负责这个槽位的节点。无论是读和写,都需要找到槽位对应的节点。下面具体介绍一下的集群处理指令时的操作
从上图可以看到和我们预想的差不多:
MOVED
错误给redis-cli,然后客户端会根据MOVED
提供的信息转向正确的节点举个例子,6370要执行两个指令(槽位我是乱写的):
127.0.0.1:6370 > GET i
"200" #在当前节点直接执行
127.0.0.1:6370 > SET new 100
-> Redirected to slot [15045] located at 127.0.0.1:6371
OK
127.0.0.1:6371 > GET new
100
这里面的三个重点:
MOVED
错误是什么?包含什么消息?执行了什么?节点使用下面这个算法来计算键对应的槽位:
def slot_number(key):
return CRC16(key) & 16383
CRC16(key)语句用与计算key的CRC-16校验和,而&16383适用于算出一个介于0 ~ 16383之间的证书作为key的槽位:
127.0.0.1:6370 > CLUSTER KEYSLOT "Im"
(integer) 2655
127.0.0.1:6370 > CLUSTER KEYSLOT "best"
(integer) 10040
KEYSLOT
的伪代码如下
def CLUSTER_KEYSLOT(key):
// 计算槽位
slot = slot_number(key)
// 将槽位返回给客户端
reply_client(slot)
当节点计算出key多数的slot之后,节点就会检查自己的clusterState.slots
数组中的index i,判断key所在的slot是否由自己负责:
clusterState.slots[i]
等于clusterState.myself,就表示slot i由当前节点负责,节点可以直接执行客户端发送的命令clusterNode
,节点会根据指向的clusterNode
结构所记录的节点的IP和PORT,向客户端发送MOVED
错误,指引客户端转向至正确处理该slot的节点。 如下图,slot 2655指向的就是myself节点,当前节点直接执行即可。slot 15045是另外的节点,所以需要返回给客户端MOVED
错误。
MOVED
错误 当节点发现键所在槽并非由自己负责处理的时候,节点就会向客户端返回一个MOVED
错误,指引客户端转向至正在负责该slot的节点。
下面为MOVED
的格式
# : key所在slot
# : 负责处理slot的节点的IP和PORT
MOVED <slot> <ip>:<port>
MOVED 15045 127.0.0.1:6371
当客户端接收到节点返回的MOVED
的错误时,客户端会根据MOVED
错误中的IP和PORT,转向至负责处理改slot的节点,并向该节点重新发送之前想要执行的命令。
以上面redirect的例子为例:
127.0.0.1:6370 > SET new 100
-> Redirected to slot [15045] located at 127.0.0.1:6371
OK
一个集群客户端通常与集群终端额多个几点创建套接字连接,而所谓的节点转向实际上就是换一个套接字来发送命令。
MOVED
错误一般在集群模式下会被隐藏,而是向上面的例子,直接转向到对应的节点。如果想要查看这个错误的话,需要使用单机模式进入,也就是redis-cli的时候不要带-c
,那么以上面的例子来说就会返回
(error) MOVED 15045 127.0.0.1:6371
注意,单机和集群模式下的数据库方面有一个区别是,集群节点只能使用0号数据库,而不是向单机那样0-15
这篇文档,我们了解了集群是如何处理由客户端发过来的指令的,再结合一下前两篇的数据结构,就会很容易理解。这篇文档的场景是普遍场景,就好比写代码时自己测试一点问题都没有,但是如果节点发生槽转移呢,或者重新分配槽呢?针对这个一个异常行为,我们下节分析。
系列文章: