Redis(三十三):集群——ASK错误

上面已经知道了集群是如何执行命令与进行重新分片的。

当集群中的服务器接收到键值对,会计算键值对所在的槽,判断这个槽是不是自己管,不是的话,就发送给对应的clusterNode,也知道了,集群是如何进行重新分片的。

ASK错误

前面提到过,集群可以进行重新分片,即将源节点的槽全部转移给目标节点管理,总共有6个步骤

  1. redis-trib使用命令让目标节点准备好从源节点导入槽
  2. redis-trib使用命令让源节点准备好将槽中的节点发送至目标节点
  3. redis-trib使用命令让源节点发送最多count个属于槽的键值对的键名
  4. redis-trib使用命令让源节点将选中的键原子地从源节点转移到目标节点(通过遍历第三步发送来的键名)
  5. redis-trib重复3和4,直到槽中的所有键值对都被发送完
  6. redis-trib向集群中的任意一个节点发送命令,通知该节点已经将槽从源节点转移给目标节点了,这一指派信息会通过消息发送到整个集群,然后集群中的所有结点都会对应修改clusterNode结构

那么在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现下面的这种状态

属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面(即部分迁移)

那么此时,客户端如果向源节点发送键值对命令,而该键值对所在的槽恰好是正在迁移的槽,那怎么办?

  • 源节点会首先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令
  • 如果找不到,那么就代表这个键很有可能被迁移到目标节点去了,源节点发发送一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令
  • ASK错误是不是跟MOVED错误很像,但ASK错误是针对重新分片过程,而MOVED错误只是节点不管理这个槽
    在这里插入图片描述
    注意,ASK错误跟MOVED错误也是被隐藏起来的,客户端接收到ASK错误也是不会打印出来的

Cluster SetSlot Importing命令的实现

cluster setslot importing是进行重新分片的第一步,redis-trib向目标节点发送该命令,准备好接收源节点的指定槽。

关于被导入的数组,是会被记录在目标节点的importing_slots_form数组中的,这个数组是一个clusterNode数组,是用来记录当前节点正在从哪些节点导入槽

typedef struct clusterState(
	//..
    clusterNode *importing_slots_from[16384];
    //..
)clusterState;

可以看到这是一个clusterNode数组,但容量却是16384,对应的是槽的数量,使用索引i记录槽

举个栗子

假如importing_slots_from[i]指向的不是空,而是一个具体的clusterNode结构,那么就代表当前节点正在从这个clusterNode对应的节点导入槽i。

在对集群进行重新分片的时候,第一个命令是

cluster setslot <i> importing <source_id>

i代表的是16384个槽中的哪个槽,source_id是代表节点在集群里面的runid

这条命令的作用其实就是,当目标节点接收到这条命令,会修改importing_slots_from数组,在索引为i的位置上,放入source_id对应的clusterNode结构
在这里插入图片描述

cluster setslot migrating命令的实现

cluster setslot migrating命令是对于第二个步骤,就是redis-trib向源节点发送该命令,让源节点准备发送槽的count个键值对。

在clusterState结构里面是使用一个migrating_slots_to数组来记录了当前节点正在迁移至其他节点的槽,也是一个clusterNode数组

typedef struct clusterState(
	//...
    clusterNode *migrating_slots_to[16384];
    //...
)clusterState;

这个与importing_slots_from一样,也是使用索引代表槽,然后对应数组的位置有没有节点,去代表该槽是否正在被发送中,发送给里面对应的clusterNode

在对集群进行重新分片的时候,会向源节点发送命令

cluster setslots <i> migrating <target_id>

i就代表槽的位置,target_id对应目标节点在集群中的runid。

当源节点收到该命令的时候,就会在migrating_slots_to[i]里保存target_id对应的custerNode节点
在这里插入图片描述
总体还是跟importing_slots_to很类似的

ASK错误

前面提到过,如果节点收到一个关于键Key的命令请求,会先去计算这个键属于的是哪个槽,然后看这个槽是不是被委派给当前节点,如果是,就自己进行执行,如果不是就发送MOVED指令,指引客户端去找正确的节点。

假设,现在槽是委派给当前节点的,那么当前节点就回去数据库(集群节点的数据库只会使用0号数据库)里找,找的时候会先检查自己的clusterState.migrating_slots_to[i](i是指定的槽)是否为空,如果为空就代表该槽没有进行转移,去数据库里面找,如果不为空,那么发送一个ASK错误,指引客户端到导入槽i的节点去查找键key

接收到ASK错误的客户端会根据错误提供的IP地址和端口号,转向至正在导入槽的目标节点,然后首先向目标节点发送一个ASKING命令,之后再重新发送自己想要执行的命令
在这里插入图片描述

ASKING命令

ASKING命令唯一要做的就是打开发送该命令的客户端的REDIS_ASKING标识

def ASKING():
	#打开REDIS_ASKING标识
	client.flags |= REDIS_ASKING
	#向客户端返回OK回复
	reply("ok")

在一般情况下,客户端向节点发送一个关于槽i的命令,而槽又不是被委派给这个节点的,那么是会返回一个MOVED错误给客户端的,但如果此时该节点的ckusterState.importing_slots_from[i]显示节点正在导入槽i,而且发来的命令带有REDIS_ASKING标识,那么节点将破例执行这个关于槽i的命令一次。
在这里插入图片描述
这里要注意的是,客户端的REDIS_ASKING标识是一个一次性的标识,当节点执行了一个带有REDIS_ASKING标识的客户端发送的命令之后,客户端的REDIS_ASKING标识就会被移除,以后的命令都不会带有该标识了

ASK错误和MOVED错误的区别

ASK错误和MOVED错误都会导致客户端转向,他们的区别如下所示

  • MOVED错误代表槽的负责权已经从一个节点转移到了另一个节点;在客户端收到关于槽i的MOVED错误之后,客户端下次的每次遇到关于槽i的命令请求时,都可以直接将命令请求发送至MOVED错误所指向的节点(因为已经建立了套接字连接,而且记录了该键是在之前MOVED错误所指向的节点处)
  • 与此相反,ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施,在客户端收到关于槽i的ASK错误之后,客户端只会在接下来的一次命令请求中将关于槽i的命令请求发送至ASK错误所指示的节点,但这种转向客户端并不会记录下来,所以不会对客户端今后发送关于槽i的命令产生任何的影响,客户端发送请求仍然会请求到目前负责处理槽i的节点,除非ASK、MOVED错误出现。

MOVED错误的过程
在这里插入图片描述
ASKING错误的过程
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值