Redis Cluster 数据分片( 二 )


当节点 A 通过消息从节点 B 那里接收到节点 B 的 slots 数组时,节点 A 会在自己的 clusterState.nodes 字典中查找节点 B 对应的 clusterNode 结构,并对结构中的 slots 数组进行保存或者更新 。
因为集群中的每个节点都会将自己的 slots 数组通过消息发送给集群中的其他节点,并且每个接收到 slots 数组的节点都会将数组保存到相应节点的 clusterNode 结构里面,因此,集群中的每个节点都会知道数据库中的 16384 个槽分别被指派给了集群中的哪些节点 。
记录集群所有槽的指派信息Redis 集群的每个节点都会记录集群所有槽的指派信息
clusterState 结构中的 slots 数组记录了集群中所有 16384 个槽的指派信息:
typedef struct clusterState {// ...clusterNode *slots[16384];// ...} clusterState;slots 数组包含 16384 个项,每个数组项都是一个指向 clusterNode 结构的指针:

  • 如果 slots[i] 指针指向 NULL,那么表示槽 i 尚未指派给任何节点 。
  • 如果 slots[i] 指针指向一个 clusterNode 结构,那么表示槽 i 已经指派给了 clusterNode 结构所代表的节点 。
如果只将槽指派信息保存在各个节点的 clusterNode.slots 数组里,会出现一些无法高效地解决的问题,而 clusterState.slots 数组的存在解决了这些问题:
  • 如果节点只使用 clusterNode.slots 数组来记录槽的指派信息,那么为了知道槽 i 是否已经被指派,或者槽 i 被指派给了哪个节点,程序需要遍历 clusterState.nodes 字典中的所有 clusterNode 结构,检查这些结构的 slots 数组,直到找到负责处理槽 i 的节点为止,这个过程的复杂度为 O(N),其中 N 为 clusterState.nodes 字典保存的 clusterNode 结构的数量 。
  • 而通过将所有槽的指派信息保存在 clusterState.slots 数组里面,程序要检查槽 i 是否已经被指派,又或者取得负责处理槽 i 的节点,只需要访问 clusterState.slots[i] 的值即可,这个操作的复杂度仅为 O(1) 。
clusterState.slots 数组记录了集群中所有槽的指派信息,而 clusterNode.slots 数组只记录了 clusterNode 结构所代表的节点的槽指派信息,这是两个 slots 数组的关键区别所在 。
客户端向集群的节点发送命令在对数据库中的 16384 个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了 。
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:
  • 如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令 。
  • 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个 moved 错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令 。
1、计算键属于哪个槽节点使用以下算法来计算给定键 key 属于哪个槽:
def slot_number(key):return CRC16(key) & 16383其中 CRC16 (key) 语句用于计算键 key 的 CRC-16 校验和,而 &16383 语句则用于计算出一个介于 0 至 16383 之间的整数作为键 key 的槽号 。
使用 cluster keyslot < key > 命令可以查看一个给定键属于哪个槽,这个命令就是通过调用上面给出的槽分配算法来实现的 。
2、判断槽是否由当前节点负责处理当节点计算出键所属的槽 i 之后,节点就会检查 clusterState.slots 数组中的项 i,判断键所在的槽是否由自己负责: