redis集群是一种redis提供的“分布式的数据库”方案,它通过分片来进行数据共享,并提供复制和故障转移功能。
集群的作用?
集群的组成
【1】一个redis集群通常由多个node节点组成,一个节点其实就是一个运行在“集群模式”下的redis服务器,redis服务器会在启动时根据cluster-enabled参数来决定这个redis服务器是否成为一个节点,否则他只是一个普通的单机的redis服务器。
【2】如果开启了这个参数,成为了一个节点,在刚开始的时候,每个节点都是相互独立的,都是处于一个只包含自己的集群当中。想要组建一个真正的集群,需要把许多的节点连接起来。客户端向节点A发送cluster-meet B_ip B_port命令,可以把B节点“拉进“A节点的集群中,这样A,B就在一个集群里了。
【3】redis集群通过”分片“来保存数据库里面的键值对:集群的整个数据库被分成16384个槽(slot),即每个键值对都在其中的某个槽里面,集群里面的每个节点都可以负责0个或最大16384个槽。
如果所有16384个槽都有节点负责处理,此时集群就进入”上线“状态。只要有一个槽没有人”负责“,那么集群就是”下线“状态。
集群的上线(槽的指派)
【1】首先是通过cluster-meet来将不同的redis服务器连接到同一个集群里面,然后我们需要进行“槽指派”,也就是把不同的槽分配给不同的“节点”。通过向“节点”发送cluster addslots命令,我们可以给槽来指派节点。
【2】一旦将数据库里面的16384个槽全都指派完毕,集群就会进入“上线”状态,这时客户端就可以向集群中的节点发送命令了。一旦发送的命令和数据库键有关,那么接收这个命令的节点就会计算出命令要处理的键属于那个槽:
1、如果这个槽正好被指派给了这个节点,那么节点直接执行这个命令
2、如果键所在的槽被指派给了别的节点,那么节点会向当前客户端返回一个MOVED错误,然后客户端根据MOVED错误提供的信息,自动转向到正确的节点。MOVED错误的格式如下:MOVED 槽号 ip:端口号。
槽的分配算法
这个算法就是用来计算一个键(key)属于哪一个槽的算法。很简单:CRC16(key)&16383。
CRC16(key):就是计算键key的CRC16校验和,然后&16383就可以计算出一个0~16383之间的整数,作为key的槽号。
判断槽是否由当前节点负责以及MOVED错误
每个节点有一个cluster-state结构体,里面的myself指向了本节点的信息:ip地址,端口号等等,然后保存着所有槽的指派信息(也就是每个槽由哪个节点负责,以及本节点负责哪些槽),这样这个节点只需要查询myself指向的节点和这个槽对应的节点是否是同一个就可以知道自己是否负责这个槽。如果自己不负责,他也可以查询到这个槽对应的节点信息(IP+端口号),然后通过MOVED错误返回给客户端。
复制与故障转移
这个就是集群模式的高可用性的表现。具体来说就是集群中的节点可以分为主节点和从节点。其中主节点用于处理对应的槽的数据,从节点则用于复制某个主节点。一旦他复制的主节点下线了,它自己会成为主节点代替原来的主节点继续处理槽的数据,原本复制旧的主节点的其他从节点也就转而复制这个新的主节点(这就是故障转移),新的主节点 会向集群广播一条pong消息,来让其他节点刷新关于这个节点的信息,告诉别人他已经变成了主节点。然后原来的主节点再次上线之后,他会成为这个新主节点的从节点。
重新分片的实现与ACK错误
redis的重新分片实际上是:将原本由某个节点负责的槽指派给另一个节点,然后槽对应的键值对也从一个节点转移到另一个节点。具体的完成是由redis-trib负责执行。
如果节点A正在向节点B迁移槽i,那么当节点A没能在自己的数据库中找到命令指定的键(key)的时候,他就会客户端返回一个ack错误,指引用户到节点B里面查找指定的键key。利用这个ack错误,客户端就可以转向到新的节点里里重新执行命令。
ack错误和moved错误的区别?
两者都会导致客户端的转向,从一个节点转向另一个节点。
1、moved表示的是槽的负责权已经从一个节点转移到了另一个节点,moved的转向是永久性的转向,也就是说在客户端收到关于槽i的moved错误之后,下次再次遇到关于槽i的命令请求时,都会转向到moved错误所指向的新节点。
2、ack错误只是两个节点在迁移槽的过程当中的临时性的转向。也就是说客户端只会在接收到ACK的下一次命令请求中把有关于槽i的指令转向到新的节点,对于后面的关于槽i的命令不会产生影响,除非再次遇到ack错误,否则不会转向。
两个端口
在 哨兵系统 中,节点分为 数据节点 和 哨兵节点:前者存储数据,后者实现额外的控制功能。在 集群 中,没有数据节点与非数据节点之分:所有的节点都存储数据,也都参与集群状态的维护。为此,集群中的每个节点,都提供了两个 TCP 端口:
gossip协议和广播
【1】广播是指向集群内所有节点发送消息。优点 是集群的收敛速度快(集群收敛是指集群内所有节点获得的集群信息是一致的),也就是说可以快速的将消息传播到整个集群,缺点 是每条消息都要发送给所有节点,CPU、带宽等消耗较大。
【2】Gossip 协议的特点是:在节点数量有限的网络中,每个节点都 按照某种规律“随机” 的与部分节点通信。Gossip 协议的 优点 有负载 (比广播) 低、去中心化、容错性高 (因为通信有冗余) 等;缺点 主要是集群的收敛速度慢(也就是说需要较长的时间消息才能传播到整个集群)。gossip协议实际上是由下面的meet,ping,pong消息来实现的
五种消息
1、meet消息:之前讲过客户端向节点A发送cluster-meet B_ip B_port命令,可以把B节点“拉进“A节点的集群中。实际上节点A接收到客户端的cluster-meet命令时,就会向节点B发送meet消息,请求接收者(B)加入到发送者(A)的集群中。
2、ping消息:集群里面每个节点每秒钟都会从已知节点列表里面随机选出5个节点,然后从里面选出最长时间没有发送过ping消息的节点发送ping消息,以此来检测被选中的节点是否在线。
此外,如果节点A最后一次收到节点B的pong消息的时间,距离当前时间超过了节点A的cluster-node-timeout设置时长的一半,那么节点A也会向节点B发送ping消息,这样做的目的是防止长时间随机选择没有选中节点B作为ping消息的发送对象,而导致的节点B的信息更新滞后。
3、pong消息:PONG 消息封装了自身状态数据。当接收到ping/meet消息之后,接收者会返回给发送者一个pong消息,为了表示自己已经确认收到。另外,节点也可以向集群广播自己的pong消息,来让集群里面的其他节点刷新关于这个节点的认识。比如当故障转移成功之后,从节点变成主节点就会广播一条pong消息。
4、fail消息:当一个主节点A判断另外一个主节点B下线了,他就会向集群广播一条节点B的FAIL消息,所有接收到消息的节点,都会将节点B标记为下线。
5、publish消息:当结点收到publish命令时,节点会执行这个命令,然后向集群广播一条publish消息,所有接收到publish消息的节点都会执行publish命令。