在redis持久化方式一文中,我们已经提到为了防止数据丢失,redis提供了RDB和AOF两种方式持久化数据,将内存的数据持久化到磁盘上。但是当出现服务器出现故障,比如服务磁盘坏掉导致数据不可恢复时。那又该怎么办呢?这时候就需要进行数据备份,将数据存储在多台服务器上。为了解决以上这个问题,现在就让我来跟大家聊一聊redis的三种集群模式-主从模式,哨兵模式和redis-clust模式都是进行处理的以及各自的特点。

1.主从模式

主从模式主要原理是主从复制,当然这三种方式都是采用主从复制。Redis虽然读取写入的速度都特别快,但是也会产生性能瓶颈,特别是在读压力上。为了分担压力,Redis支持主从复制。Redis的主从结构可以是一主一从,一主多从或级联结构,复制类型可以根据是否是全量而分为全量同步和增量同步。

1.1 主从模式结构图

redis 集群类型 redis集群有哪几种模式_redis 集群类型


以上为主从模式(一主多从)的简单架构图。主从模式里使用一个redis实例作为主机(master),用来支持数据的写入和读取操作,其余多个实例作为备份机(slave),支持读取及master的数据同步,在整个架构里,master和slave实例里的数据完全一致。

1.2 主从模式原理

主从模式的数据存储需要在不同时机段进行不同的赋值操作,这里主要有类:全量同步和增量同步。

1.2.1 全量同步

Redis全量同步复制一般发生在slave的初始阶段,这时slave需要将master上的数据都复制一份。以下是全量同步的大致流程图:

redis 集群类型 redis集群有哪几种模式_数据持久化_02


具体的过程如下:

  1. 从节点启动时,向主节点发送SYNC命令;
  2. 主节点接收到SYNC命令后,开始在后台执行保存快照的命令生成RDB文件,并使用缓冲区记录此后执行的所有写命令;
  3. 主节点快照完成后,将快照文件和所有缓存命令发送给集群内的从节点,并在发送期间继续记录被执行的写命令;
  4. 从节点收到快照后,丢弃所有的旧数据,载入收到的数据;
  5. 主节点快照发送完毕后开始向从节点发送缓冲区中的写命令;
  6. 从节点载入快照文件后,开始接收命令请求,执行接收到的主节点缓冲区的写命令。
  7. 从节点完成上面的数据初始化后就可以开始接受用户的读请求了。

1.2.1 增量同步

增量复制实际上就是在从节点初始化完成后开始正常工作时主节点发生写操作同步到从节点的过程。增量复制的过程主要是主节点每执行一个写命令就会向从节点发送相同的写命令,从节点接受并执行写命令,从而保持主从一致。
所以redis的主从同步策略是这样的:主从同步刚连接的时候进行全量同步;全量同步结束后开始增量同步。当前然如果有需要的话,从节点在任何时候都可以发起全量同步,其主要策略就是无论如何首先会尝试进行增量同步,如果不成功,则会要求从节点进行全量同步,之后再进行增量同步。

1.3 主从模式特点

主从模式具有以下特点:

  • 采用异步复制,可以一主多从,从还可以连接其他的从;
  • 主从复制对于master来说是非阻塞的,也就是说slave在进行主从复制的过程中,master依然可以处理请求;
  • 主从复制对于slave来说也是非阻塞的,也就是说slave在进行主从复制的过程中也可以接受外界的查询请求,只不过这时候返回的数据不一定是正确的。为了避免这种情况发生,可以在slave的配置文件中配置,在同步过程中阻止查询;

主从模式提高了Redis服务的扩展性,避免单节点问题,另外也为数据备份冗余提供了一种解决方案。为了降低主redis服务器写磁盘压力带来的开销,可以配置让主redis不再将数据持久化到磁盘,而是通过连接让一个配置的从redis服务器及时的将相关数据持久化到磁盘,不过这样会存在一个问题,就是主redis服务器一旦重启,因为主redis服务器数据为空,这时候通过主从同步可能导致从redis服务器上的数据也被清空。

2.哨兵模式

主从模式中,每个客户端连接redis实例时都指定了ip和端口号。如果所连接的redis实例因为故障下线了,则无法通知客户端连接其他客户端地址,于是redis集群迎来了哨兵模式,它可以监控Redis系统的运行状态,并做相应的响应。

2.1 哨兵模式架构图

哨兵模式基本架构图如图所示:

redis 集群类型 redis集群有哪几种模式_后端_03


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

  1. 监控所有Redis节点是否正常运行;
  2. master故障后可以通过投票机制,从slave中选举出新的master,保证集群正常运行。

通过哨兵可以管理多个Redis服务器,它的任务主要有以下三个:

  1. 监控:哨兵会不断的检测master和slave之间是否运行正常;
  2. 提醒:当监控的某个Redis出现问题,哨兵可以通过API向管理员或其他应用程序发送通知;
  3. 故障迁移:当一个master不能正常工作时,哨兵会开始一次自动故障迁移操作,它会将失效master的其中一个slave提升为master,并让失效master和其他slave改为连接新的master,当客户端试图连接失效的master时,集群也会向客户端返回新的master地址,使得集群可以使用新的master代替失效的master。

2.2 哨兵模式原理

小兵模式是基于以上三个任务来完成对各节点的监控和发现的,接下来从每个任务出发来分析一下基本原理。

(1)监控

每隔1秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测。每个哨兵节点每10秒会向主节点和从节点发送info命令获取最新拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到。

redis 集群类型 redis集群有哪几种模式_redis 集群类型_04


(2)提醒

每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断。这里说明两个词,一个是主观下线,一个是客观下线。

redis 集群类型 redis集群有哪几种模式_java_05


主观下线:哨兵节点每隔1秒对主节点和从节点、其它哨兵节点发送ping做心跳检测,当这些心跳检测时间超过down-after-milliseconds时,哨兵节点则认为该节点错误或下线,这叫主观下线。这可能会存在错误的判断;

客观下线:当主观下线的节点是主节点时,此时该哨兵节点会通过指令sentinel is-masterdown-by-addr寻求其它哨兵节点对主节点的判断,当超过quorum(法定人数)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线。

redis 集群类型 redis集群有哪几种模式_redis_06


当某个节点被标记为客观下线时,就需要重新选举领导者,redis采用Raft算法实现选举机制,选出一个哨兵节点(哨兵也有主节点)来完成转移和通知,领导者哨兵选举流程如下:

  1. 每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
  2. 当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;
  3. 如果哨兵3发现自己在选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举,最后肯定会有一个票数最对的成为领导者。这里建议哨兵至少3个并且是奇数。
  4. 选举完领导者之后,故障转移的操作则由领导者完成。

(3)故障迁移

  1. 由Sentinel节点定期监控发现主节点是否出现了故障,sentinel会向master发送心跳PING来确认master是否存活
  2. 如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了,会要求其他sentinel确认该节点是否丢失,如果确认,则认为是客观下线,的确不可用了,开始进行故障转移;
  3. 当主节点出现故障,此时3个Sentinel节点共同选举了Sentinel3节点为领导,负载处理主节点的故障转移;
  4. 由Sentinel3领导者节点执行故障转移,过程和主从复制一样,但是自动执行。

redis 集群类型 redis集群有哪几种模式_java_07

2.3 哨兵模式特点

哨兵模式的出现虽然解决了主从模式中master节点宕机不能自主切换(即高可用)的问题,但是依然存在一些问题:
(1)主从服务器的数据要经常进行主从复制,这样会造成性能下降;
(2)当主服务器宕机后,从服务器切换成主服务器的那段时间,服务是不可用的;
(3)随着业务的逐渐增长,不可避免需要对当前业务进行扩容。扩容又分为水平扩容和垂直扩容,垂直扩容指通过增加master内存来增加容量;水平扩容指通过增加节点来进行扩容,即在当前基础上再增加一个master节点。虽然垂直扩容方式很便捷,不需要添加多余的节点,但是机器的容量是有限的,最终还是需要通过水平扩容方式来解决。而水平扩容涉及到数据的迁移,且迁移过程中又要保证服务的可用性。因此,数据能不迁移就尽量不要迁移。显然,哨兵模式无法满足这种情形,redis cluster应运而生。

3.redis-cluster模式

Redis在3.0版本开始正式引用集群特性,Redis集群是一个分布式,高容错的内存K/V系统。

3.1 redis-cluster模式结构图

redis 集群类型 redis集群有哪几种模式_java_08


redis cluster模式采用了无中心节点的方式来实现,每个主节点都会与其它主节点保持连接。节点间通过gossip协议交换彼此的信息,同时每个主节点又有一个或多个从节点。

3.2 redis-cluster模式原理

3.2.1 数据存储

Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。如下所示:

redis 集群类型 redis集群有哪几种模式_java_09


群中的每个节点至少一个备用的redis服务,这个集群就不会那么容易挂掉。这里设置的是Master1负责0-5460号哈希槽,Master2负责5461-10922号哈希槽,Master3负责10922~16383号哈希槽,前面说过了大家可以依据自己的设定自行进行调整,每个节点会保存一份数据分布表,节点会将自己的槽信息发送给其他节点,节点间不停的传递数据分布表。需要注意的是,在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权。

了解了哈希槽以后,那么Redis集群是怎么依据这些存储数据的呢?当我们的存取的key到达的时候,redis会根据特定算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

3.2.2 故障处理

redis cluster中主节点故障处理方式与哨兵模式较为相像,当约定时间内某节点无法与集群中的另一个节点顺利完成ping消息通信时,则将该节点标记为主观下线状态,同时将这个信息向整个集群广播。如果一个节点收到某个节点失联的数量达到了集群的大多数时,那么将该节点标记为客观下线状态,并向集群广播下线节点的fail消息。然后立即对该故障节点进行主从切换。等到原来的主节点恢复后,会自动成为新主节点的从节点。如果主节点没有从节点,那么当它发生故障时,集群就将处于不可用状态。需要注意的是如果有一半以上的节点去ping一个节点的时候没有回应,集群就认为这个节点宕机了,然后去连接它的备用节点。如果某个节点和所有从节点全部挂掉,我们集群就进入faill状态。还有就是如果有一半以上的主节点宕机,那么我们集群同样进入faill状态。

3.2.3 扩容问题

在哨兵模式中我们在扩容的时候遇到了问题,那么cluster中我们如何动态上线某个节点呢?当集群中加入某个节点时,哈希槽又是如何来进行分配的?当集群中加入新节点时,会与集群中的某个节点进行握手,该节点会把集群内的其它节点信息通过gossip协议发送给新节点,新节点与这些节点完成握手后加入到集群中,然后集群中的节点会各取一部分哈希槽分配给新节点。比如我们此时要新增一个节点Master4,如下图:

redis 集群类型 redis集群有哪几种模式_redis 集群类型_10


我们将Master1,Master2,Master3的部分槽重新分配给Master4。当集群中要删除节点时,只需要将节点中的所有哈希槽移动到其它节点,然后再移除空白(不包含任何哈希槽)的节点就可以了。

3.3 redis-cluster模式特点

Redis集群有以下几个重要的特征::

  • Redis集群的分片特征在于将空间拆分为16384个槽位,某一个节点负责其中一些槽位;
  • Redis集群提供一定程度的可用性,可以在某个节点宕机或者不可达的情况继续处理命令;
  • Redis集群不存在中心节点或代理节点,集群的其中一个最重要的设计目标是达到线性可扩展性。