Redis Cluster 数据分片
作者:互联网
介绍 Redis Cluster
Redis 集群是 Redis 提供的分布式数据库方案, 集群通过分片(sharding) 来进行数据共享, 并提供复制和故障转移功能。
节点
一个 Redis 集群通常由多个节点(node) 组成, 在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中, 要组建一个真正可工作的集群, 我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作可以使用 cluster meet 命令来完成, 该命令的格式如下:cluster meet < ip > < port >。节点通过握手来将其他节点添加到自己所处的集群当中。向一个 node 节点发送 cluster meet 命令, 可以让 node 节点与 ip 和 port 所指定的节点进行握手(handshake),当握手成功时, node 节点就会将 ip 和 port 所指定的节点添加到 node 节点当前所在的集群中。
一个节点就是一个运行在集群模式下的 Redis 服务器,Redis 服务器在启动时会根据 cluster-enabled 配置选项是否为 yes 来决定是否开启服务器的集群模式。启动服务器时,服务器判断是否开启集群模式:
- 如果 cluster-enabled 选项的值为 yes,则开启服务器的集群模式,成为一个节点;
- 如果 cluster-enabled 选项的值不为 yes,则开启服务器的单机(stand alone)模式,成为一个普通的 Redis 服务器。
节点会继续使用 redisServer 结构来保存服务器的状态, 使用 redisClient 结构来保存客户端的状态,至于那些只有在集群模式下才会用到的数据,节点将它们保存到了cluster.h/clusterNode 结构、 cluster.h/clusterLink 结构,以及 cluster.h/clusterState 结构里面。
槽指派
Redis 集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。
通过向节点发送 cluster addslots 命令,我们可以将一个或多个槽指派(assign)给节点负责:cluster addslots < slot > [slot ...]。
在 cluster addslots 命令执行完毕之后,节点会通过发送消息告知集群中的其他节点,自己目前正在负责处理哪些槽。也就是说,每个节点都会记录哪些槽指派给了自己,而哪些槽又被指派给了其他节点。
集群的在线状态:
- 上线状态:当数据库中的 16384 个槽都有节点在处理时,集群处于上线状态(ok);
- 下线状态:相反地,如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态(fail)。
节点保存槽指派信息
Redis 集群的每个节点都会记录自己负责处理哪些槽。
clusterNode 结构的 slots 属性和 numslot 属性记录了节点负责处理哪些槽:
struct clusterNode { |
|
// ... |
|
unsigned char slots[16384/8]; |
|
int numslots; |
|
// ... |
|
}; |
slots 属性是一个二进制位数组,这个数组的长度为 16384 / 8 = 2048 个字节,共包含 16384 个二进制位。Redis 以 0 为起始索引,16383 为终止索引,对 slots 数组中的 16384 个二进制位进行编号,并根据索引 i 上的二进制位的值来判断节点是否负责处理槽 i:
- 如果 slots 数组在索引 i 上的二进制位的值为 1,那么表示节点负责处理槽 i。
- 如果 slots 数组在索引 i 上的二进制位的值为 0,那么表示节点不负责处理槽 i。
numslots 属性则记录节点负责处理的槽的数量,也即是 slots 数组中值为 1 的二进制位的数量。
节点之间传播槽指派信息
一个节点除了会将自己负责处理的槽记录在 clusterNode 结构的 slots 属性和 numslots 属性之外,它还会将自己的 slots 数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽。
当节点 A 通过消息从节点 B 那里接收到节点 B 的 slots 数组时, 节点 A 会在自己的 clusterState.nodes 字典中查找节点 B 对应的 clusterNode 结构,并对结构中的 slots 数组进行保存或者更新。
因为集群中的每个节点都会将自己的 slots 数组通过消息发送给集群中的其他节点, 并且每个接收到 slots 数组的节点都会将数组保存到相应节点的 clusterNode 结构里面, 因此, 集群中的每个节点都会知道数据库中的 16384 个槽分别被指派给了集群中的哪些节点。