Redis的多机功能:复制、哨兵和集群

330 阅读8分钟

Redis作为一个支持分布式的数据库,多机操作显得格外重要,本文就Redis多机功能中的复制、哨兵与集群功能做简单的分析。

复制

在Redis中,用户可以通过执行 SLAVEOF 命令或者设置 slaveof 选项,让一个服务器去复制另一个服务器,我们称被复制的服务器是主服务器,对主服务器进行复制的服务器是从服务器

image.png

进行复制的服务器之前数据保持相同的状态,概念上将这种现象称为“一致”。下面简单记录一下Redis如何通过PSYNC复制数据库的。

PSYNC

在Redis 2.8之前,一直是在用SYNC命令进行同步,但是由于每次断线之后的完整重同步机制被人诟病。PSYNC命令具有完整重同步和部分重同步两种模式:

  • 在初次复制时,让主服务器创建并发送RDB文件,并向从服务器发送保存在缓冲区里面的写命令,来完成完整重同步;
  • 在断线后重复制的情况下,当从服务器在断线后重连主服务器时,在条件允许的情况下,支持从服务器只处理断线期间的写命令;

image.png

部分重同步

了解PSYNC同步的流程,下面简单介绍下部分重同步的原理,分为三部分:

  • 复制偏移量
  • 复制积压缓冲区
  • 服务器运行ID

复制偏移量

执行复制的双方都会分别维护一个复制偏移量,通过对比主从数据库的复制偏移量,Redis就可以很容易的知道主从数据库是否处于一致状态:

image.png

  • 如果主从服务器处于一致状态,那么主从服务器两者的偏移量一定是相同的;
  • 反之,如果主从服务器偏移量不相同,那么可以判断主从服务器不在一致状态。

复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度的先进先出队列,默认大小为1MB。当主服务器进行命令传播时,不仅会将命令 发送给所有从服务器,还会将写命令入队到复制积压缓冲区:

image.png

当从服务器重连主服务器时,从服务器会通过PSYNC将自己的复制偏移量传送给主服务器,主服务器会判断:

  • 如果 offect 偏移量之后的数据仍然存在复制积压缓冲区中,则执行部分同步操作;
  • 反之,主服务器将对从服务器执行完整的重同步操作;

服务器运行ID

每隔Redis服务器都有自己的运行ID,每个从服务器对主服务器进行操作,都会保存一份主服务器的运行ID,在断线重连时,从服务器会发送这个ID给主服务器,如果ID不一致(说明经过了主从切换,某个从服务器升级为主服务器),那么就会执行完整重同步,否则就会执行部分重同步。

心跳检测

在命令传播阶段,每1s从服务器就会向主服务器发送命令:

 REPLCONF ACK 101 # 101是复制偏移量

主从服务器就可以通过 REPLCONF ACK 命令检查两者之间网络是否正常,如果主服务器1s没有收到从服务器的 REPLCONF ACK 命令,那么就知道链接出问题了

同时,心跳检测也可以用于检测命令丢失(通过偏移量)。

哨兵

哨兵(Sentinel)是Redis高可用的解决方案,由一个或多个哨兵组成的哨兵系统可以监视任意多个主服务器,以及这些主服务器树下的所有从服务器,在被监视的主服务器处于下线状态时,自动将从服务器升级为新的主服务器,然后由新的主服务器继续处理命令请求,这就是故障转移。这个过程可以展示成:

image.png

哨兵集群

单哨兵或者多哨兵都可以组成一个哨兵系统,哨兵可以通过命令连接共同监控服务器,如下图所示:

image.png 每个哨兵都可以通过命令连接与其他哨兵进行信息交换。

主观下线与客观下线

默认情况下,哨兵会每秒一次的频率向所有与它创建命令连接的主服务器、从服务器和哨兵发送PING命令,并通过返回的信息判断实例是否在线。

哨兵配置文件中的down-after-milliseconds选项制定了哨兵判断实例进入主观下线的时间长度,如果在down-after-milliseconds毫秒内,连续发送并无有效回复,哨兵就会判断当前实例进入主观下线状态。

当一个哨兵将主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,会向同样监视这个主服务器的哨兵进行询问,客观下线的标准就是,当有 N 个哨兵实例时,最好要有 N/2 + 1 个实例判断主库为主观下线,才能最终判定主库为客观下线。并对主服务器进行故障转移。

选举头领

当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个哨兵会进行协商,选举一个头领(某个哨兵获得集群里哨兵的半数“选票”,就可以成为头领),并由头领对下线的主服务器进行故障转移。

选举主服务器

故障转移时,如果有多个从服务器,如何从中选主服务器呢?

Redis首先提供了一种过滤机制:

  • 删除已经处于下线或者断线状态的从服务器;
  • 删除最近5s没有回复头领哨兵INFO命令的从服务器;
  • 删除与断线主服务器断开连接超过down-after-milliseconds * 10毫秒的从服务器;

然后,头领哨兵会根据从服务器优先级最大的服务器设置为主服务器。

如果有多个从服务器有最高优先级,那么头领哨兵会选取其中偏移量最大的从服务器(跟主服务器数据最接近)。

如果存在多个最高优先级并且偏移量最大的服务器,那么就会选其中ID最小的服务器为主服务器。

集群

Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享。

集群建立

一个Redis集群通常由多个节点组成,但是初始状态下,所有节点都是独立的,要组件一个真正可工作的集群,需要将各个独立的节点连接起来:

image.png

槽指派

Redis集群通过分片的方式来保存数据,集群的整个数据库被分为16384个槽(slot),数据库16384个槽都有节点在处理时,集群处于上线(ok)状态;相反的,如果有一个槽没有节点处理,那么集群处于下线状态(fail)。

通过 CLUSTER ADDSLOTS 命令,如我们可以将一个槽指派给节点负责:

  • 将0-5000分配给server1;
  • 将5001-10000分配给server2;
  • 将10001-16384分配给server3;

重新分片

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

重新分片的操作可以是在线的,在重新分片过程中,集群不需要下线。

复制与故障转移

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

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

  • 复制下线主节点的所有从节点里,会有一个从节点被选中;
  • 被选中的从节点会执行SLAFEOF no one命令,成为新的主节点;
  • 新的主节点会撤销所有对已下线主节点的槽指派,并将所有槽指派给自己;
  • 新的主节点向集群进行广播,通知其他节点该节点已经变成主节点;
  • 新的主节点开始接收和自己负责处理的槽有关的命令请求。

总结

通过复制、哨兵与集群,Redis实现了多机相关的功能,提供了一个高可用的多机数据库实现。