Redis高可用——集群

209 阅读10分钟

前言

Redis集群是Redis提供的分布式数据方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。

本文将对节点、槽指派、命令执行、重新分片、转向、故障转移、消息等各个方面进行介绍。

概念

节点

一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,我们必须将各个独立的节点连接起来,构成一个包含多个节点的集群。

连接各个节点的工作可以使用CLUSTER MEET命令来完成,该命令的格式如下:

CLUSTER MEET <ip> <port>

向一个节点node 发送CLUSTER MEET命令,可以让node节点与ip和port所指定的节点进行握手(handshake ),当握手成功时,node节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。

  • 一个节点就是运行在集群模式下的Redis服务器,可以在服务器启动时根据 cluster-enables配置选项是否为yes来决定是否开启服务器的集群模式。

image.png

槽指派

Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为 16384 个槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群的每个节点可以处理0个或最多16384个槽。

当数据库中的16384个槽都有节点在处理时,集群处于上线状态( ok );相反地如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态( fail )。

假设现在有由三个节点组成的集群,可以通过向节点发送 CLUSTER ADDSLOTS命令,将一个或多个槽指派给节点负责:

CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000
  • 将槽 0-5000 指派给节点1负责
  • 将槽 5001-10000 指派给节点2负责
  • 将槽 10001-16384 指派给节点2负责

当以上槽都指派给相应的节点后,集群进入上线状态,可以通过 CLUSTER INFO命令查看集群状态。

一个节点除了会将自己负责的槽记录在 clusterNode 结构的 slots 属性和 numslots 属性外,它还会将自己的 slots 数组通过消息发送给集群中其他节点,告知其他节点自己负责处理的槽。所以,集群中每个节点都会知道数据库中的所有槽分别被指派给了集群中的哪个节点。

image.png

命令执行

在对数据库中的16384个槽都进行了指派以后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。

当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:

  • 如果键所在的槽正好指派给了当前节点,那么节点直接执行这个命令
  • 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向至正确的节点,并再次发送之前想要执行的命令

image.png

重新分片

Redis集群的重新分片操作可以将任意数量已指派给某个节点的槽改为指派给另一个节点,并且相关槽所属的键值对也会从源节点被移动到目标节点。

重新分片操作可以在线运行,在重新分片过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

实现原理

Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。

redis-trib对集群的单个槽 slot 进行重新分片的步骤如图所示:

image.png

1、redis-trib对目标节点发送 CLUSTER SETSLOT <slot> IMPORTING <source_id>命令,让目标节点准备好从源节点导入属于槽slot的键值对

2、redis-trib对源节点发送CLUSTER SETSLOT <slot> MIGRATING <target_id>命令,让源节点准备好将属于槽slot的键值对迁移(migrate)至目标节点。

3、redis-trib向源节点发送CLUSTER GETKEYSINSLOT <slot> <count>获得最多count个属于槽slot的键值对的键名

4、对于步骤3获得的每个键名,redis-trib都向源节点发送一个MIGRATE<target_ip> <target_port> <key_name> 0 <timeout>命令,将被选中的键原子地从源节点迁移至目标节点

5、重复执行步骤3和步骤4,直到源节点保存的所有属于槽slot的键值对都被迁移至目标节点为止。

6、redis-trib 向集群中的任意一个节点发送CLUSTER SETSLOT<slot> NODE<target_id>命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点

ASK错误

在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。

当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:

  • 源节点会先在自己的数源据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令
  • 相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令

image.png

复制与故障转移

Redis集群中的节点又分为主节点和从节点,其中主节点用于处理槽,从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。

举个例子,对于包含7000、7001、7002、7003四个主节点的集群来说,我们可以将7004、7005两个节点添加到集群里面,并将这两个节点设定为节点7000的从节点,如图(图中以双圆形表示主节点,单圆形表示从节点)。

image.png

如果这时,节点7000进入下线状态,那么集群中仍在正常运作的几个主节点将在节点7000的两个从节点——节点7004和节点7005中选出一个节点作为新的主节点,这个新的主节点将接管原来节点7000负责处理的槽,并继续处理客户端发送的命令请求。

例如,如果节点7004被选中为新的主节点,那么节点7004将接管原来由节点7000负责处理的槽0至槽5000,节点7005也会从原来的复制节点7000,改为复制节点7004

如果在故障转移完成之后,下线的节点7000重新上线,那么它将成为节点7004的从节点。

image.png

故障转移

当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的步骤:

  • 复制下线主节点的所有从节点里面,会有一个从节点被选中

  • 被选中的从节点会执行SLAVEOF no one命令,成为新的主节点

  • 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己

  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已由从节点变为了主节点,并且这个主节点已经接管了原本由下线节点负责处理的槽

  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成

消息

集群中的各个节点通过发送和接收消息(message)来进行通信,我们称发送消息的节点为发送者(sender),接收消息的节点为接收者(receiver)

节点发送的消息主要有以下五种:

  • MEET消息:当发送者接到客户端发送的CLUSTER MEET命令时,发送者会向接收者发送MEET消息,请求接收者加入到发送者当所处的集群里面

  • PING 消息:集群里的每个节点默认每隔一秒钟就会从已知节点列表中随机选出五个节点,然后对这五个节点中最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中的节点是否在线。除此之外,如果节点A最后一次收到节点B发送的 PONG消息的时间,距离当前时间已经超过了节点A的cluster-node-timeout选项设置时长的一半,那么节点A也会向节点B发送PING消息,这可以防止节点A因为长时间没有随机选中节点B作为PING消息的发送对象而导致对节点B的信息更新滞后

  • PONG消息:当接收者收到发送者发来的MEET消息或者PING消息时,为了向发送者确认这条MEET消息或者PING消息已到达,接收者会向发送者返回一条PONG消息。另外,一个节点也可以通过向集群广播自己的PONG消息来让集群中的其他节点立即刷新关于这个节点的认识,例如当一次故障转移操作成功执行之后,新的主节点会向集群广播一条PONG消息,以此来让集群中的其他节点立即知道这个节点已经变成了主节点,并且接管了已下线节点负责的槽。

  • FAIL消息:当一个主节点A判断另一个主节点B已经进人FAIL状态时,节点A会向集群广播一条关于节点B的FAIL消息,所有收到这条消息的节点都会立即将节点B标记为已下线。

  • PUBLISH消息:当节点接收到一个PUBLISH命令时,节点会执行这个命令,并向集群广播一条PUBLISH消息,所有接收到这条PUBLISH消息的节点都会执行相同的PUBLISH命令。

小结

本文主要对Redis集群机制的概念进行讲解,包括:节点、槽指派、命令执行、重新分片、ASK错误、复制与故障转移、消息等,如对 Redis感兴趣可继续关注本专栏。