【Redis】Redis集群架构剖析(2):槽位

  在第一篇Redis集群架构剖析(1):认识cluster一篇中,我们对cluster有了初步的了解。知道如何启动一个集群,存储集群信息的数据结构长什么样的。虽然我们创建好了集群,但是集群的状态还是下线的。其实,这是因为集群中的三个节点,都没有负责处理任何槽位。也可以理解为,没有分配给集群节点,谁存什么键值范围的数据。在开始之前,依旧可以先思考下面的问题:

  • 如何给节点指派槽位的?
  • 节点是如何记录槽指派信息的?
  • 节点又是如何记录集群槽指派信息的?
  • 节点如何告诉其他节点,我的槽指派信息?

槽(slot)

  Redis集群通过分片的方式来保存数据库中的键值对,集群的整个数据库被分为16384个槽,数据库中的每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个。

只有当数据库中的16384个槽都有节点在处理时,集群才处于上线状态。

槽指派

  通过向节点发送CLUSTER ADDSLOTS命令,我们可以将一个或多个槽指派给节点负责:

CLUSTER ADDSLOTS <slot> [slot...]

#例子
127.0.0.1:6370 > CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000

  在三个节点都指派好槽位之后,集群进入上线状态。在讲完下面的数据结构之后,再讲一下ADDSLOTS的实现细节。

  槽指派可以通过CLUSTER ADDSLOTS,也可以通过redis-cli create cluster创建集群时,自动分配槽位,见:

redis_cluster_部署

槽指派信息数据结构

  clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:

struct clusterNode {
    //...
    unsigned char slots[16384/8] 
    
    int numslots; //记录slots里为1的个数
    //...
}

  slots是一个二进制位数组(bit array),这个数组的长度为16348/8=2048个字节,共包含16384个二进制位。

  redis以0为其实索引,16384为终止索引,根据索引i上的二进制位的值来判断是否处理槽i。

  • 如果slots数组在索引i上的二进制位的值为1,那么表示节点负责处理槽i。

  • 如果slots数组在索引i上的二进制位的值为0,那么表示节点不负责处理槽i。

  以下图为例,表示该节点负责处理8,9,10,11,12,13,14,15槽位,numslots就为8:

在这里插入图片描述

广播节点槽指派信息

  节点会告诉集群里的其他节点我当前存的槽位信息

在这里插入图片描述

  6371收到6370的槽指派信息后,节点6371会在自己的clusterState.nodes字典中查找节点6370对应的clusterNode结构,并对结构中的slots数组进行保存或者更新。可以看到集群内的节点会互相发送消息,我现在的槽指派信息是哪些。这样子,集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪个节点。

集群记录槽指派信息

  如果只将槽指派信息保存在各个节点的clusterNode.slots数组里,效率来说不是很高,,通过遍历clusterState.nodes找到负责处理槽i的节点,时间复杂度为O(N)。因此,clusterState中存了一个全局的slots,记录了集群中所有16384个槽的指派信息。

typedef struct clusterState {
    //...
    clusterNode *slots[16384];
    //...
}

  每个项指向一个clusterNode结构的指针,如果为NULL表示还未分配,反之是一个clusterNode的话,就表示该槽已经分配。下图为集群分配好槽位后,clusterState.slots的示意图。红线表示要查找槽位5002的路线图。

在这里插入图片描述

  clusterState.slots数组记录了集群中所有槽的指派信息,而clusterNode.slots数组只记录clusterNode结构所代表的节点的槽位信息,这是这两个slots最大的区别。clusterNode.slots存在的意义,在于当需要发送某个节点的槽指派信息给其他节点的时候,可以把整个节点的槽指派信息发送出去,而不是遍历clusterState.slots来拼接出来。

CLUSTER ADDSLOTS命令的内部实现

可以先看一下CLUSTER ADDSLOTS的伪代码,看下具体在做什么

def CLUSTER_ADDSLOTS(*all_input_slots):
	
	// 遍历所有输入槽,检查它们是否都是未指派的槽位
	// 可以直接根据节点记录的集群槽指派信息来查
	for i in all_input_slots:
		
		// 只要有一个槽被分配了,那么会向客户端返回错误,并终止命令执行
		if clusterState.slots[i] != NULL:
				reply_error()
                return
                    
	// 如果槽都还未指派,那么再遍历所有输入槽,将这些槽位指派给当前节点
    for i in all_input_slots:
		
		// 设置clusterState结构的slots数组,对应index指向当前节点
		clusterState.slots[i] = clusterState.myself
		
		// 设置当前clusterNode的slots,将对应二进制位改成1
		setSlotBit(clusterState.myself.slots, i)

下图是对一个节点指派槽位2的示意图,根据伪代码,就是设置图中红色方框的位置。

在这里插入图片描述

CLUSTER ADDSLOTS命令执行完毕之后,节点会通过发送消息告知集群中的其他节点,自己目前正在负责处理的槽位。


在分配好所有的槽位之后,集群的状态从下线变成了上线,意味着,我们可以通过redis-cli和redis-server交互了。但是集群收到命令之后会怎么处理呢?请听下回分解。


系列文章

  1. 【Redis】Redis集群架构剖析(1):认识cluster
  2. 【Redis】Redis集群架构剖析(3):集群处理redis-cli指令
  3. 【Redis】Redis集群架构剖析(4):槽位迁移,重新分配
  4. 【Redis】Redis集群架构剖析(5):复制与故障转移
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值