一、Redis集群 - 主从模式

Redis集群总共有三种模式:主从模式、哨兵模式和分片集群。主从模式是其中最简单的模式,这种模式中,Redis被分为主节点(master)和从节点(slave/replica) 。主节点可以进行读、写操作,而从节点一般只能进行读操作,如果在从节点上进行写操作,Redis会报错。主节点和从节点会进行数据同步,使节点上的数据保持一致。

1、搭建主从架构

redis分布式原理视频 redis分布式架构_redis


假设我们现在有3台计算机A、B、C作为Redis节点,我们要用它们来搭建主从架构,把A作为master(主节点)。

① 基本配置:首先,我们要在这3个节点上都安装。为保证这3个节点的Redis状态是一样的,因此把A的Redis配置文件复制并覆盖另外两个节点的Redis配置文件。然后启动3个节点上的Redis。

② 开启主从关系:要配置主从可以使用replicaof或者slaveof命令,这两条命令是一样的,只是Redis在5.0版本之后把从节点的名字从slave改成了replica。

有两种方式可以进行配置:临时和永久。

  • 永久配置:在从节点的Redis配置文件(通常是redis.conf)中添加一行配置:slaveof <masterip> <masterport>。其中,后两个参数分别是主节点的ip地址和Redis端口号。
  • 临时配置:使用redis-cli客户端连接到redis服务,执行slaveof命令(重启后失效):slaveof <masterip> <masterport>。

2、主从数据同步

有两种同步方式:全量同步和增量同步。

全量同步

主从第一次建立连接时,会进行一次全量同步,将master节点的所有数据都拷贝给slave节点。此外,当增量同步失败的时候,也会进行全量同步。全量同步的流程如下(从上到下依次执行):

redis分布式原理视频 redis分布式架构_redis分布式原理视频_02


在第二阶段中,master节点会进行一次bgsave操作生成RDB文件,再把这个RDB文件发给slave节点。我们知道,在bgsave期间,Redis仍然能进行服务,如果在这段时间内,master执行了写操作,那么主从节点之间的数据就会产生差异。因此,master节点会把bgsave执行期间的所有命令都记录到repl_backlog(上图把名字写错了)文件中,然后在发送RDB文件之后把repl_backlog中的命令发送给从节点(图中的2.1.1操作)。从节点会执行这些命令,从而保证主从间的数据是一致的。

Q:一个问题,master是如何得知slave节点是第一次与它建立连接的呢?
A:关于这个问题,首先我们要了解两个概念Replication Id和offset:

  • Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid。
  • offset:偏移量。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。

slave要进行数据同步时,需要告诉master自己的Replication Id和offset。

由于slave原本也是一个master,因此它也有自己的Replication Id和offset,并且和master的不一样。

master判断发现slave发送来的Replication Id与自己的不一致,说明这是一个全新的slave,就知道要做全量同步了。此外,master会将自己的Replication Id和offset都发送给这个slave,因此从,经过第一次全量同步之后,slave的Replication Id就和master的一样了。

将刚才的图升级一下:

redis分布式原理视频 redis分布式架构_插槽_03


总结一下:

redis分布式原理视频 redis分布式架构_redis_04


增量同步

通过bgsave生成RDB是一个比较消耗性能的操作。因此除了第一次做全量同步,其它大多数时候slave与master都是做增量同步。

增量同步就是只更新slave与master存在差异的部分数据(与offset有关了)。

redis分布式原理视频 redis分布式架构_Redis_05


首先,我们来再次认识一下repl_backlog文件。

这个文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。

repl_baklog中会记录Redis处理过的命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset:

redis分布式原理视频 redis分布式架构_分布式_06


如果master和slave节点的offset没有相差超过一圈,也就是说slave少于master的那部分数据在repl_backlog中都能找到的时候,主从节点间的数据同步就会通过增量同步来完后,也就是从repl_backlog文件中找出slave的offset到master的offset的这部分内容(是一些命令),然后发送给slave节点,slave收到之后再执行这些命令。

但如果master和slave节点的offset相差太多了,导致slave缺的数据已经被新数据覆盖了,如下图所示,那么就会把增量同步改成全量同步操作。

redis分布式原理视频 redis分布式架构_Redis_07


redis分布式原理视频 redis分布式架构_插槽_08


3、主从同步优化

redis分布式原理视频 redis分布式架构_redis分布式原理视频_09


主-从-从链式结构:

redis分布式原理视频 redis分布式架构_redis_10


4、小结

redis分布式原理视频 redis分布式架构_Redis_11


二、Redis集群 - 哨兵模式(Sentinel)

1、认识哨兵模式

在主从模式中,如果master节点故障了,Redis就无法对外提供写操作,除非我们手动将某一个slave节点设置为新的master节点。

为解决这个问题,我们可以使用哨兵模式,哨兵能够在后台监控master节点,当发现master节点发生故障时,哨兵会自动将某一个slave节点升级为master节点(可以设置选择slave节点的规则)。

哨兵模式的结构图如下所示(Sentinel即哨兵进程,我们在此处设置了3个哨兵进程):

redis分布式原理视频 redis分布式架构_Redis_12


Sentinel在本质上是一个独立的进程。Sentinel的作用如下:

  • 监控:Sentinel 会不断检查您的master和slave是否按预期工作。
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主。
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端。

2、Sentinel是如何进行监控的
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个节点发送ping命令,并根据节点的回应情况将它们分为主观下线客观下线两种状态。

  • 主观下线:如果某Sentinel节点发现某节点未在规定时间响应,则认为该节点主观下线。
  • 客观下线:若超过指定数量(quorum)的Sentinel都认为该节点主观下线,则认为该节点客观下线。quorum值最好超过Sentinel实例数量的一半。

3、哨兵模式是如何进行故障恢复的
一旦发现master故障,Sentinel会在slave中选择一个作为新的master,选择的流程如下:

  • 首先,判断各个slave节点与master节点断开时间的长短,如果超过指定值(down-after-milliseconds * 10)则排除该slave节点。
  • 然后判断slave节点的slave-priority值(在slave节点的Redis配置文件中进行配置),值越小表示优先级越高。
  • 如果slave-prority一样,则判断slave节点的offset值,值越大说明数据越新,则优先级越高。
  • 最后是判断slave节点的运行id大小,值越小则优先级越高。

Q:选出新的master节点之后,要如何实现切换呢?
A:流程如下(假设当前选中了节点slave1作为新的master):

  • sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master。如果没有设置哨兵模式,也可以手动执行这条命令slaveof no one来使一个slave节点变成master节点。
  • sentinel给其它所有的slave发送slaveof 192.168.150.101 7002命令(这里的IP和端口号是slave1节点的IP和端口号),让这些slave成为新master的从节点,开始从新的master上同步数据。
  • 最后,sentinel将故障节点(原先的master)标记为slave,当故障节点恢复后会自动成为新的master的slave节点。

redis分布式原理视频 redis分布式架构_Redis_13


4、搭建哨兵集群

①准备配置文件:新建3个目录(如果要配置的哨兵数量为3),每个目录对应一个哨兵,每个目录下新建一份新的Sentinel配置文件(sentinel.conf),如下所示:

redis分布式原理视频 redis分布式架构_redis_14


redis分布式原理视频 redis分布式架构_redis_15


②通过redis命令启动哨兵:

redis分布式原理视频 redis分布式架构_分布式_16


5、总结

redis分布式原理视频 redis分布式架构_分布式_17


6、RedisTemplate的哨兵模式

Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。通过RedisTemplate配置哨兵集群的流程如下:

①引入依赖。

redis分布式原理视频 redis分布式架构_redis_18


②配置Redis的地址。

redis分布式原理视频 redis分布式架构_分布式_19


③在启动类中添加一个bean,这个bean配置了Redis的读写策略。

redis分布式原理视频 redis分布式架构_分布式_20

redis分布式原理视频 redis分布式架构_Redis_21


补充:哨兵除了监控Redis节点,也会监控其他哨兵。

三、Redis集群 - 分片集群模式(Sentinel)

1、认识分片集群

redis分布式原理视频 redis分布式架构_redis_22


为解决这两个问题,我们可以采用分片集群,分片集群的结构如下图所示:

redis分布式原理视频 redis分布式架构_redis分布式原理视频_23


分片集群具有以下特点:

  • 集群中有多个master,每个master保存不同数据(将海量数据分散地存储在多个master中)。
  • 每个master可以有自己的slave节点。
  • master之间通过ping监测彼此健康状态(实现了哨兵的功能)。
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点。

2、散列插槽
在Redis中,有一个散列插槽(hash slot)的概念(类似于哈希表中)。Redis会根据key的有效部分计算插槽值,然后把数据存储到对应的插槽中去。
计算插槽值的方式:
①首先获取key的有效部分。

  • 如果key中包含"{}",且“{}”中至少包含1个字符,则“{}”中的部分是有效部分,如{phone}_redmi,这个key的有小部分为“phone”。
  • key中不包含“{}”,则整个key都是有效部分,如phone_redmi
    ②根据有效部分计算插槽,计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。

数据key不是与节点绑定,而是与插槽绑定。Redis总共有16384个插槽,Reids会将这些插槽分配给各个节点,当我们读取或者写入数据时,会根据数据的key计算出插槽,然后到对应的节点上进行操作。

【个人有个猜想,当我们没有使用集群时,Redis节点只有一个,所以所有的插槽都分配给该节点了,因此我们没有感受到插槽这个东西。未经验证,仅做记录。】

举个例子:

redis分布式原理视频 redis分布式架构_redis分布式原理视频_24


小结:

redis分布式原理视频 redis分布式架构_redis分布式原理视频_25


3、集群伸缩(添加或删除集群节点)

假如现在我们要往集群中加一个新的master节点,并向其中存储 num = 10。那么我们需要实现两个功能:

  • 添加一个节点到集群中。
  • 将部分插槽分配到新插槽(插槽转移)。

添加节点到集群中:

redis分布式原理视频 redis分布式架构_redis_26


比如:redis-cli --cluster add-node 192.168.150.101:7004 192.168.150.101:7001

这里把节点192.168.150.101:7004加入到节点192.168.150.101:7001所在的集群中。作为master节点加入。因为如果要作为slave节点加入的话,在这句命令中还需要加上--cluster-slave--cluster-master-id \<arg>.

转移插槽:

前面提到,我们要把keyi为num的数据存储到新加入的这个节点中,因此我们需要先看看num在哪个插槽上,然后把包括num的插槽在内的一部分插槽分配给新节点。分配的时候,这些插槽可能在原先的节点上已经有数据了,因此需要把这些数据转移过来。

具体例子如下(通过计算得知num的插槽为2765,因此我们把0~3000的插槽从原节点(7001)转移到新节点(7004)来):

①与7001建立连接:

redis分布式原理视频 redis分布式架构_Redis_27


得到如下反馈,询问我们要转移多少插槽

redis分布式原理视频 redis分布式架构_Redis_28


输入我们要转移的插槽数量(3000),得到如下反馈,询问我们要把这些插槽转移到哪个节点去

redis分布式原理视频 redis分布式架构_redis分布式原理视频_29


输入7004节点的id。----补充说明----

如何得到节点id?在第①步操作(建立连接时),当我们输入并执行reshard命令之后会显示各个节点的信息,其中包括各个节点的id,如下图所示:

redis分布式原理视频 redis分布式架构_Redis_30

----补充说明----

redis分布式原理视频 redis分布式架构_插槽_31


输入7004节点的id之后,得到反馈(如上图所示)询问这些插槽该从哪个节点移动过来。我们有3种输入方式:

redis分布式原理视频 redis分布式架构_分布式_32


这里我们要从7001获取,因此填写7001的id:

redis分布式原理视频 redis分布式架构_redis_33


填完后,点击done,这样插槽转移就准备好了。最后再按照提示输入yes即可完成转移。补充:通过help可以查看操作集群的命令:

redis分布式原理视频 redis分布式架构_插槽_34


4、故障恢复

当集群中有master节点故障时,其他master节点如果确定该master下线了,就会自动提升该master的一个slave节点为新的master(与哨兵模式一样)。

手动进行故障转移

利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:

redis分布式原理视频 redis分布式架构_分布式_35


redis分布式原理视频 redis分布式架构_redis_36


5、搭建分片集群

Redis5.0以后,集群管理已经集成到了redis-cli中,格式如下(后面的这些IP和端口代表要加入集群的Redis节点):

redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003

命令说明:

  • redis-cli --cluster或者./redis-trib.rb:代表集群操作命令
  • create:代表是创建集群
  • –replicas 1或者–cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master

6、ReidsTemplate访问分片集群

与哨兵模式基本一致。

redis分布式原理视频 redis分布式架构_插槽_37