集群
槽指派
记录节点的槽指派信息。
clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:
struct clusterNode {
// ...
unsigned char slots[16384/8];
int numslots;
// ...
}
slots属性是一个二进制位数组(bit array),这个数组的长度位16384/8=2048个字节,共包含16384个二进制位。Redis以0为起始索引,16383为终止索引,对slots数组中的16384个二进制位进行编号,并根据索引i上的二进制位的值来判断节点是否负责处理槽i:
- 1.如果slots数组在索引i上的二进制位的值位1,那么表示节点负责处理槽i
- 2.如果slots数组在索引i上的二进制位的值为0,那么表示节点不负责处理槽i
因为取出和设置slots数组中的任意一个二进制位的值的复杂度仅为O(1),所以对于一个给定节点的slots数组来说,程序检查节点是否负责处理某个槽,又或者将某个槽指派给节点负责,这两个动作的复杂度都是O(1).
至于numslots属性则记录节点负责处理的槽的数量,也即是slots数组中值位1的二进制位的数量。比如说,例子中的节点处理的槽数量分别为8、6
例子
- 举个例子。如图展示了一个slots数组的示例:这个数组索引0至索引7上的二进制位的值都为1,其余所有二进制位的值都为0,这表示节点负责处理槽0至槽7
- 如图展示了另一个slots数组示例:这个数组索引1、3、5、8、9、10上的二进制位的值都为1,而其余所有二进制位的值都为0,这表示节点负责处理1、3、5、8、9、10
传播节点的槽指派信息
一个节点除了会将自己负责处理的槽记录在clusterNode结构的slots属性和numslots属性之外,他还会将自己的slots数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽.
例子
- 举个例子,对于前面展示的7000、7001、7002三个节点的集群来说:
1.节点7000会通过消息向节点7001和节点7002发送自己的slots数组,以此来告知两个节点,自己负责处理槽0至槽5000,如图所示
2.节点7001会通过消息向节点7000和节点7002发送自己的slots数组,以此来告知两个节点,自己负责处理槽5001至槽10000,如图所示
3.节点7002会通过消息向节点7000和节点7001发送自己的slots数组,以此来告知两个节点,自己负责处理槽10001至槽16383,如图所示
记录集群所有槽的指派消息
clusterState结构中的slots数组记录了集群中所有16384个槽的指派信息:
typedef struct clusterState{
// ...
clusterNode *slots[16384];
// ...
} clusterState;
slots数组包含16384个项,每个数组项都是一个指向clusterNode结构的指针:
- 1.如果slots[i]指针指向NULL,那么表示槽i尚未指派给任何节点
- 2.如果slots[i]指针指向一个clusterNode结构,那么表示槽i已经指派给了clusterNode结构所代表的节点
如果只将指派信息保存在各个节点的clusterNode.slots数组里,会出现一些无法高效地解决的问题,而clusterState.slots数组的存在解决了这些问题:
- 1.如果节点只使用clusterNode.slots数组来记录槽的指派信息,那么为了知道槽i是否已经被指派,或者槽i被指派给了哪个节点,程序需要遍历clusterState.nodes字典中所有的clusterNode结构,检查这些结构的slots数组,直到找到负责处理槽i的节点为止,这个过程的复杂度为O(N),其中N为clusterState.nodes字典保存的clusterNode结构的数量
- 2.而通过将所有槽的指派信息保存在clusterState.slots数组里面,程序要检查槽i是否已经被指派,又或者取得负责处理槽i的节点,只需要访问clusterState.slots[i]的值即可,这个操作的复杂度仅为O(1)
例子
- 举个例子,对于7000、7001、7002三个节点来说,它们的clusterState结构的slots数组将会是如图所示的样子:
1.数组项slots[0]至slots[5000]的指针都指向7000的clusterNode结构,表示槽0至5000都指派给了节点7000
2.数组项slots[5001]至slots[10000]的指针都指向代表节点7001的clusterNode结构,表示槽5001至槽10000都指派给了节点7001
3.数组项slots[10001]至slots[16383]的指针都指向代表节点7002的clusterNode结构,表示槽10001至槽16383都指派给了节点7002
- 举个例子,对于上图中的slots数组来说,如果程序需要直到槽10002被指派给了哪个节点,那么只需要访问数组项slots[10002],就可以马上直到槽10002被指派给了节点7002,如图所示
注意
要说明的一点是,虽然slutserState.slots数组记录了集群中素有槽的指派信息,但使用clusterNode结构的slots数组来记录单个节点的槽指派信息仍然是有必要的:
- 1.因为当程序需要将某个节点的槽指派信息通过消息发送给其他节点时,程序只需要将相应节点的clusterNode.slots数组整个发送出去就可以了
- 2.另一方面,如果Redis不使用clusterNode.slots数组,而单独使用clusterNode.slots数组的话,那么每次要将节点A的槽指派信息传播给其他节点时,程序必须先遍历整个clusterState.slots数组,记录节点A负责处理哪些槽,然后才能发送节点A的槽指派信息,这比直接发哦是那个clusterNode.slots数组要麻烦和低效得多。clusterState.slots数组记录了集群中所有槽的指派信息,而clusterNode.slots数组只记录了clusterNode结构所代表的节点的槽指派信息,这时两个slots数组的关键区别所在