集群
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。
从本篇文章开始总结集群相关的内容,集群相关的知识点包括七部分,分别是节点、槽指派、在集群中执行命令、重新分片、ASK错误、复制与故障迁移、消息。
1. 节点
一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是互相独立的,他们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,必须将各个独立的节点连接起来,构成一个包含多个节点的集群。
连接各个节点的工作可以使用CLUSTSER MEET命令完成,将当前节点与目标节点进行握手,握手成功后,就会将目标节点添加到当前节点所在的集群中。
# 查看目前集群各节点情况
CLUSTER NODES
# 将节点7001添加到本节点所在的集群中
CLUSTER MEET 127.0.0.1 7001
CLUSTER MEET 127.0.0.1 7002
1.1. 启动节点
Redis服务器是否开启集群模式是由配置文件中cluster-enabled
选项决定的,运行在集群模式下的Redis服务器会继续使用所有在单机模式中使用的服务器组件。
一个节点的当前状态保存在clusterNode
结构体中,如节点创建时间、节点名字、节点当前配置纪元、节点IP地址和端口号等。每个节点会使用一个clusterNode结构体来记录自己的状态,并为集群中所有其他节点(主节点和从节点)都创建一个clusterNode
结构,保存其他节点的状态。
1.2. CLUSTER MEET
节点A执行命令CLUSTER MEET <ip> <port>
,通过向节点A发送CLUSTER MEET命令,客户端可以让节点A将另一个节点B添加到节点A所在的集群中,节点A和节点B进行握手,来确认彼此的存在。
节点A和节点B完成握手后,会将节点B的信息通过Gossip协议传播到集群中的其他节点,让其他节点也和节点B进行握手,一段时间后,节点B被集群中所有节点认识。
2. 槽指派
Redis集群通过分片方式保存数据库中键值对,集群的整个数据库被分为16384个槽,数据库中每个键都属于这16384个槽其中的一个。当数据库中16384个槽都有节点在处理时,集群处于上线状态(ok);相反,如果数据库中任何一个槽没有得到处理,集群处于下线状态(fail)。
通过向节点发送CLUSTER ADDSLOTS
命令,可以将一个或多个槽指派给节点负责。CLUSTER ADDSLOTS 0 1 2 3 ... 5000
可以将槽0至槽5000指派给当前节点负责,CLUSTER ADDSLOTS 5001 5002 5003 ... 10000
可以将槽5001至槽10000指派给第二个节点负责,CLUSTER ADDSLOTS 10001 10002 10003 ... 16383
可以将剩余槽指派给第三个节点负责。至此,集群进入上线状态,可以使用CLUSTER INFO
命令查看。
2.1. 节点的slots信息
clusterNode结构的slots属性和numslot属性记录了节点该负责处理那些槽。slots是一个位图(二进制数组),数组长度为16384/8=2048个字节,共包含16384个二进制位。Redis以0为起始索引,16383为终止索引,索引i对应的值,用来判断节点是否负责处理槽i,1表示节点负责槽i,0表示节点不负责槽i。
下图表示,当前节点负责负责处理槽0至槽7。
2.2. 传播节点的slots信息
一个节点除了将自己负责的slots,记录在clusterNode中的slots属性和numsslots属性外,还会将slots数组通过消息发送给集群中的其他节点,通告自己负责哪些slots。
集群中的每个节点都会将自己的slots数组通过消息发送给集群中的其他节点,并且每个接收到数组的节点都会将数组保存到clusterNode中。最终,集群中每个节点都会知道数据库中的16384个槽分别被指派给了集群中哪些节点。
2.3. 记录集群所有slots信息
集群中的16384个slots的指派信息,记录在clusterState的slots数组中,slots数组中元素是clusterNode类型的指针。如果slots[i] == NULL,表示slot i没有被指派;如果slots[i] != NULL表示slot i已经被指派给该元素对应的节点。
3. 在集群中执行命令
对数据库中16384个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己。
- 如果键所在的槽正好指派给了当前节点,节点直接执行这个命令。
- 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。
4. 重新分片
Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。当集群中新加入节点时,就需要触发重新分片的操作,将一些槽指派给新加入的节点。
重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
5. ASK错误
在重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现如下情况:被迁移的一部分键保存在源节点中,另一部分键则保存在目标节点中。
此时,如果客户端向源节点发送一个命令,并且该命令要处理的键恰好就属于正在被迁移的槽时会发生如下步骤:1. 源节点会现在自己的数据库里面查找指定的键,如果找到的话,就直接执行命令。2. 如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点会向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并在此发送之前想要执行的命令。
6. 复制与故障迁移
Redis集群中的节点分为主节点(master)和从节点,其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。
7. 消息
集群中各个节点通过发送和接收消息来进行通信,称发送消息的节点为发送者,接收消息的节点为接收者。常见的消息有五种: MEET消息 、 PING消息 、 PONG消息、FAIL消息 、 PUBLISH消息。