本文由 简悦 SimpRead) 转码, 原文地址 mp.weixin.qq.com)

作者 | 莱乌

上篇《干货:送你一份新鲜出炉的面试题》里提到了 redis 集群的三种模式,私下里就这道题和几位熟悉的朋友也交流过,很多答得不是很全面。大多数人平时对 redis 的使用很熟悉,但是对于这种偏思想与设计的东西却只是略知一二。

那么,今天我们就来聊聊 redis 集群的三种模式。

画外音:事实上,这道题在小莱的面试经历中被问到的频率还是挺高的。

主从模式

1、架构图

Redis集群 - 图1

2、集群介绍

1)主从模式里使用一个 redis 实例作为主机 (master),其余多个实例作为备份机 (slave);

2)master 用来支持数据的写入和读取操作,而 slave 支持读取及 master 的数据同步;

3)在整个架构里,master 和 slave 实例里的数据完全一致;

3、主从复制原理

全量同步

  1. 当从节点启动时,会向主节点发送 SYNC 命令;
  2. 主节点接收到 SYNC 命令后,开始在后台执行保存快照的命令生成 RDB 文件,并使用缓冲区记录此后执行的所有写命令;
  3. 主节点快照完成后,将快照文件和所有缓存命令发送给集群内的从节点,并在发送期间继续记录被执行的写命令;
  4. 主节点快照发送完毕后开始向从节点发送缓冲区中的写命令;
  5. 从节点载入快照文件后,开始接收命令请求,执行接收到的主节点缓冲区的写命令。

增量同步

主从复制中因网络等原因造成数据丢失场景,当从节点再次连上主节点。如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销。

三种模式主从复制原理基本一致。

4、主节点故障处理方式

主从模式中,每个客户端连接 redis 实例时都指定了 ip 和端口号。如果所连接的 redis 实例因为故障下线了,则无法通知客户端连接其他客户端地址,因此只能进行手动操作。

5、不支持高可用

显而易见,主从模式很好地解决了数据备份的问题,但是主节点因为故障下线后,需要手动更改客户端配置重新连接,这种模式并不能保证服务的高可用。

于是 redis 集群迎来了哨兵模式……

哨兵模式

1、架构图

Redis集群 - 图2

2、集群介绍

和主从模式不一样的是,哨兵模式中增加了独立进程(即哨兵)来监控集群中的一举一动。客户端在连接集群时,首先连接哨兵,通过哨兵查询主节点的地址,然后再去连接主节点进行数据交互。

如下图所示,如果 master 异常,则会进行 master-slave 切换,将最优的一个 slave 切换为主节点。

Redis集群 - 图3

Redis集群 - 图4

同时,哨兵持续监控挂掉的主节点,待其恢复后,作为新的从节点加入集群中。

Redis集群 - 图5

3、主节点故障处理方式 / 哨兵工作方式

  1. 每个哨兵每秒向集群中的 master、slave 以及其他哨兵发送一个 PING 命令;
  2. 如果某个实例距离最后一次有效回复 ping 命令的时间超过一定值,则会被标记为主观下线;
  3. 如果 master 被标记为主观下线,那么其他正在监视 master 的哨兵以每秒的频率确认其确实进入主观下线状态,且数量达到一定值时,master 会被标记为下线,然后通知其他的从服务器,修改配置文件,让它们切换主机;
  4. 客户端在 master 节点发生故障时会重向哨兵要地址,此时会获得最新的 master 节点地址。

4、扩容问题

哨兵模式的出现虽然解决了主从模式中 master 节点宕机不能自主切换(即高可用)的问题。但是,随着业务的逐渐增长,不可避免需要对当前业务进行扩容。

常见的扩容方式有垂直和水平扩容两种方式:

  • 垂直扩容:通过增加 master 内存来增加容量;
  • 水平扩容:通过增加节点来进行扩容,即在当前基础上再增加一个 master 节点。

虽然垂直扩容方式很便捷,不需要添加多余的节点,但是机器的容量是有限的,最终还是需要通过水平扩容方式来解决。而水平扩容涉及到数据的迁移,且迁移过程中又要保证服务的可用性。因此,数据能不迁移就尽量不要迁移。

显然,哨兵模式无法满足这种情形。因此,redis cluster 应运而生。

Redis cluster 模式

1、架构图

Redis集群 - 图6

2、集群介绍

  1. redis cluster 模式采用了无中心节点的方式来实现,每个主节点都会与其它主节点保持连接。节点间通过 gossip 协议交换彼此的信息,同时每个主节点又有一个或多个从节点;
  2. 客户端连接集群时,直接与 redis 集群的每个主节点连接,根据 hash 算法取模将 key 存储在不同的哈希槽上;
  3. 在集群中采用数据分片的方式,将 redis 集群分为 16384 个哈希槽。如下图所示,这些哈希槽分别存储于三个主节点中:
  • Master1 负责 0~5460 号哈希槽
  • Master2 负责 5461~10922 号哈希槽
  • Master3 负责 10922~16383 号哈希槽

Redis集群 - 图7

  1. 每个节点会保存一份数据分布表,节点会将自己的 slot 信息发送给其他节点,节点间不停的传递数据分布表;
  2. 客户端连接集群时,通过集群中某个节点地址进行连接。客户端尝试向这个节点执行命令时,比如获取某个 key 值,如果 key 所在的 slot 刚好在该节点上,则能够直接执行成功。如果 slot 不在该节点,则节点会返回 MOVED 错误,同时把该 slot 对应的节点告诉客户端,客户端可以去该节点执行命令。

3、主节点故障处理方式

redis cluster 中主节点故障处理方式与哨兵模式较为相像,当约定时间内某节点无法与集群中的另一个节点顺利完成 ping 消息通信时,则将该节点标记为主观下线状态,同时将这个信息向整个集群广播。

如果一个节点收到某个节点失联的数量达到了集群的大多数时,那么将该节点标记为客观下线状态,并向集群广播下线节点的 fail 消息。然后立即对该故障节点进行主从切换。等到原来的主节点恢复后,会自动成为新主节点的从节点。如果主节点没有从节点,那么当它发生故障时,集群就将处于不可用状态。

4、扩容问题

在哨兵模式中我们在扩容的时候遇到了问题,那么 cluster 中我们如何动态上线某个节点呢。当集群中加入某个节点时,哈希槽又是如何来进行分配的?

当集群中加入新节点时,会与集群中的某个节点进行握手,该节点会把集群内的其它节点信息通过 gossip 协议发送给新节点,新节点与这些节点完成握手后加入到集群中。

然后集群中的节点会各取一部分哈希槽分配给新节点,如下图:

  • Master1 负责 1365-5460
  • Master2 负责 6827-10922
  • Master3 负责 12288-16383
  • Master4 负责 0-1364,5461-6826,10923-12287

Redis集群 - 图8

当集群中要删除节点时,只需要将节点中的所有哈希槽移动到其它节点,然后再移除空白(不包含任何哈希槽)的节点就可以了。

推荐阅读

《面试官:谈谈分布式锁的实现》

《干货:送你一份新鲜出炉的面试题》

《害!今晚又矫情了…..》

  • END -

Redis集群 - 图9

Redis集群 - 图10

点个「在看」和大家一起看

Redis集群 - 图11