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来决定是否开启服务 器的集群模式,如图
节点(运行在集群模式下的Redis服务器)会继续使用所有在单机 模式中使用的服务器组件,比如说:
除此之外,节点会继续使用redisServer结构来保存服务器的状态,使用redisClient结构来保存客户端的状态,至于那些只有在集群模式下才会用到的数据,节点将它们保存到了cluster.h/clusterNode结构、 cluster.h/clusterLink结构,以cluster.h/clusterState结构里面
Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个 槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。
当数据库中的16384个槽都有节点在处理时,集群处于上线状态 (ok);相反地,如果数据库中有任何一个槽没有得到处理,那么集群 处于下线状态(fail)。
clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:
slots属性是一个二进制数据(bit array),这个数组的长度为16384/8=2048个字节,共包含16384个二进制位。
Redis以0为起始索引,16384为终止索引,对slots数组中的16384个二进制进行编号,并根据索引i上的二进制位来判断节点是否负责处理槽i:
各个节点会通知其他节点本节点的槽指派信息,最终达到所有的节点都知道所有的槽指派信息。
clusterState结构中的slots数组记录了集群中所有的16384个槽的指派信息:
slots数组包含16384个项,每个数组项都是一个指向clusterNode结构的指针:
def slot_number(key):
return CRC16(key) & 16384
其中CRC16(key) 语句用于计算键key的CRC-16校验和,而&16384则用于计算出一个介于0到16384之间的整数作为键key的槽号。
集群节点保存键值对以及键值对过期时间的方式,与第9章里面介 绍的单机Redis服务器保存键值对以及键值对过期时间的方式完全相同。
节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库,而单机Redis服务器则没有这一限制。
另外除了将键值对保存在数据库里面之外,节点还会用 clusterState结构中的slots_to_keys跳跃表来保存槽和键之间的关系:
slots_to_keys跳跃表每个节点的分值(score)都是一个槽号,而每个节点的成员(member)都是一个数据库键:
通过在slots_to_keys跳跃表中记录各个数据库键所属的槽,节点可 以很方便地对属于某个或某些槽的所有数据库键进行批量操作,例如命 令CLUSTER GETKEYSINSLOT <slot> <count>
命令可以返回最多count个 属于槽slot的数据库键,而这个命令就是通过遍历slots_to_keys跳跃表来 实现的。
Redis集群的重新分片操作可以将任意数量已经指派给某个节点 (源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。
重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
操作步骤:
1)redis-trib对目标节点发送CLUSTER SETSLOT <slot> IMPORTING <source_ id>
命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。
2)redis-trib对源节点发送CLUSTER SETSLOT <sIot> MIGRATING <target id>
命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。
3)redis-trib向源节点发送CLUSTERGETKEYSINSLOT <slot> <count>
命令,获得最多count个属于槽slot的键值对的键名(key
name)
4)对于步骤3获得的每个键名,redis-tib都向源节点发送一个 MIGRATE <target_ ip> <starget_ port> <key name> 0<timeout>
命令,将被选中的键原子地从源节点迁移至目标节点。
5) 重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。
6)redis-trib向集群中的任意一个节点发送CLUSTER SETSLOT <slot> NODE <target id>
命令,将槽slot指派给目标节点,这一
指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点。
复制与故障迁移都类似于Sentinel的复制与故障迁移
都是采用Raft算法