【Redis】Redis主从架构和哨兵模式



主从架构

为什么需要Redis主从架构?

Redis 单点架构有以下问题

  • 造成服务雪崩:高并发场景下,Redis 单点故障了,导致请求到 Redis
    后都返回错误,或者请求都到数据库了,造成服务雪崩,这是不能接受的。
  • 不能进行快速转移:线上系统,Redis 单点故障了,没有其他的备份节点可用。
  • 不能快速恢复数据:Redis 是内存数据库,内存数据会自动备份到 RDB 和 AOF 文件(开启了两种持久化的情况下),当某个节点出现故障时,能将这些备份文件快速恢复到节点,将故障造成的影响降到最低。

搭建主从架构(一主二从)

在从节点开启主从复制,有 3 种方式:

  1. 配置文件 在从服务器的配置文件中加入 replicaof
  2. 启动命令 redis-server 启动命令后面加入 --replicaof
  3. 客户端命令 启动多个 Redis 实例后,直接通过客户端执行命令:replicaof ,则该 Redis 实例成为从节点。

首先下载一个单机redis服务器,然后复制两份 redis-6380.conf和redis-6381.conf文件,分别更改以下相关配置从节点:

port 6380                      # 从节点6380 (下同)
replicaof 192.168.0.60 6379    # 配置从节点访问主节点(下同)
slave-read-only yes            # 配置从节点只读操作(下同)
port 6381
replicaof 192.168.0.60 6379 
slave-read-only yes

在控制台执行 redis‐server redis-6379.conf、redis‐server redis-6380.conf、redis‐server redis-6381.conf。命令启动redis一主二从服务。
启动成功后,测试一下在主节点添加数据,从节点是否会同步主节点数据。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

往从节点添加数据是否会插入成功。
在这里插入图片描述
在这里插入图片描述
看上图,往主节点set数据后,在从节点能get到数据。往从节点set数据会提示read only。
至此搭建完成了。

主从一致原理

  1. 当我们为master节点配置了一个slave节点,不管slave是否是第一次链接上master,它都会发送一个psync命令给master请求复制数据。
  2. master收到psync命令后,会在后台进行数据持久化通过bgsave生成最近的rdb快照文件,期间Redis会继续接收其它客户端的请求,并把这些新的数据集请求缓存在内存中。
  3. 当持久化完成后,master会把这份rdb文件发送给slave,slave会把接收到的数据进行持久化生成RDB文件并加载到内存,然后master再将之前缓存在内存中最新的数据发送给slave。

当slave与master再次断开链接后,slave能自动重连master,如果master收到了多个slave并发连接请求,那它也只会持久化一次,而不是一个连接持久化一次,再把这个持久化文件发送给其他slave。

slave 做复制的时候,不会 block maste的正常工作;
slave在做复制的时候,也不会 block 对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了

Redis在2.8.18版本开始实现了无磁盘复制功能
master 在内存中直接创建 RDB,然后发送给 slave,不会在自己本地落地磁盘了
只需要在配置文件中开启 repl-diskless-sync yes 即可。

repl-diskless-sync yes

复制积压缓冲区

当有客户端的写命令请求到主节点后,主节点会将写命令写入到复制积压缓冲区
复制积压缓冲区:其实就是一个有界队列,保存着最近传播的写命令,而队列里面的每个字节都有一个偏移量标识。

复制积压缓冲区有几个特点:

  • 固定长度的队列。
  • 最近传播的写命令,默认为 1 MB 大小,可调节大小。
  • 队列中的每个字节都有对应的复制偏移量进行标识。

如下图所示,每一个字节对应一个偏移量。

在这里插入图片描述
节点重新连上主节点后,会发送 psync 命令,携带着偏移量 offset。比如 offset = 125,然后主节点拿着这个 125 去复制积压缓冲区找,125 正好在里面,然后就会执行部分复制的操作,将 125 以后的缓冲数据发送给从节点。

在这里插入图片描述
如果 offset =10,主节点拿着这个 10 去复制积压缓冲区找,发现队列中最早的 offset 是 100,所以 100 之前的字节都被覆盖了,那么子节点就不能通过复制积压缓冲区拿到完整数据,所以只能通过全量复制的方式来同步。这个时候主节点就会发送一个 FULLRESYNC的命令给子节点,告诉子节点,兄弟,你来得太晚了,只能使用全量同步的方式了。

在这里插入图片描述
不管是全量复制还是部分复制,最开始都会发送一个 psync 命令给主节点,那么主节点会根据这个命令携带的参数 runId 和 offset,来决定如何响应。

全量复制

全量复制过程总共可以分为 9 步:

  1. 从节点发送 psync 命令进行数据同步,会发送 psync 命令,来告诉主节点我想干啥。

psync 命令格式如下:

psync {runId} {offset}

runId 是 每个 Redis 实例启动时随机生成的一个 ID,用来唯一标记这台 Redis 实例。由于第一次复制时,刚启动时会随机生成 runId,只有自己知道,外人是不知道的,所以从节点是不知道主节点的 runId 的,这个时候发送 psync 命令时 runId 就是一个问号。第一次复制时,offset 默认为 -1。也就是全量复制

  1. 主节点响应从节点,开始全量复制。响应如下命令:
    FULLRESYNC runId offset
    runId 就是主节点 id,offset 为复制偏移量。
  2. 从节点收到主节点响应的 runId 和 offset,将其保存到从节点本地,这两个参数以后会用到。
  3. 主节点在后台执行 bgsave 命令保存 RDB 文件到主节点的本地。
  4. 主节点将第四步生成的 RDB 文件发送给从节点,子节点收到 RDB 文件后,保存到本地,后面会用到。这个发送的过程也可能直接超时。比如一个 6 GB 的 RDB 文件,100 MB 带宽下,至少需要 60 秒的传输时间,很容易超出默认配置的超时时间。那么从节点将放弃接收 RDB 文件,并清理已经下载的临时文件,导致全量复制失败。所以推荐不要超过 6 GB,如果 RDB 文件实在太大了,可以调大 repl-timeout 超时参数。
  5. 在第五步的时候,主节点也没有闲着,会往另外一个缓冲区写东西,就是来自客户端的写命令数据。这个缓冲区叫做:复制客户端缓冲区。等第五步完成后,主节点就把这个缓冲区的数据发送给从节点。注意:对于高流量写入的场景,很容易就把复制客户端缓冲区给占满了,如果 60 秒内缓冲区消耗持续大于 64 MB 或者直接超过 256 MB 时,主节点将直接关闭复制客户端连接,造成全量不同失败。
  6. 从节点在第五步保存完 RDB 文件后,就会把自身的旧数据清空。
  7. 节点终于可以开始加载 RDB 文件了,但是对于较大的 RDB 文件,加载 RDB 文件,进行数据恢复,还是非常耗时的,如果从节点负责响应读命令,则可能拿到过期或错误的数据。
  8. 从节点加载完 RDB 后,如果当前节点开启了 AOF 持久化功能,从节点会执行 bgrewriteof 操作,保证 AOF 持久化文件可以立刻使用。

由上面的几个步骤可以看出,全量复制是非常耗时的,可能比较大的时间开销如下:

第四步,主节点 fork 出子进程执行 bgsave 时,fork 操作耗时。
第五步,RDB 文件的网络传输时间。
第七步,从节点清空数据花时间。
第八步,从节点加载 RDB 的时间。
第九步,AOF 的重写时间。
所以除了第一次需要采用全量复制外,其他场景应该避免全量复制的发生。下面介绍另外一种复制方式,可以极大提高复制的效率。

增量复制

Redis 主从的增量复制就是指当主从之间的网络故障等原因造成持续复制中断了,当从节点再次连上主节点后,主节点就补发数据给从节点,避免了全量复制的过高开销。补发数据的来源就是复制积压缓冲的数据。

原理图如下所示:
在这里插入图片描述
增量复制总共分为六步:

  1. 当主节点之间失联后,如果时间超过了 repl-timeout 时间,主节点就认为从节点发生故障了,中断连接。
  2. 主节点其实一直都在把客户端写命令放入复制积压缓冲区,所以即使断连了,主节点还是会保留断连期间的命令,但因为队列是固定的,当写命令太多时,就会导致部分命令被覆盖了。
  3. 主从节点恢复连接。
  4. 从节点发送 psync 命令给主节点,带有 runIdoffset 参数,runId 是上一次复制时保存的主节点的 runId值,offset 是从节点的复制偏移量。
  5. 主节点接收到从节点的命令后,先判断传过来的 runId 是否和自己匹配,如果不匹配,则进行全量复制;如果 runId 匹配,则响应 CONTINUE,告诉从节点,可以进行部分复制了。我要把复制积压缓冲区的数据发给你了哦,请准备好接收。
  6. 主节点根据子节点发送的偏移量,将复制积压缓冲区的数据发送给子节点。

如果有很多slave,那为了缓解主从复制风暴(多个slave同时复制master数据,导致master压力过大),可以做下列架构,让部分slave与slave进行同步数据。
在这里插入图片描述

在主从架构中出现了宕机的情况

① Slave 宕机

在Redis中,从库重新启动会自动加入到主从架构中,自动完成同步数据

Redis 2.8之后,在从库有做持久化的前提下,如果从库在断开的期间,主库的变化不大,从库再次启动后,主库不会将已有的数据进行RDB操作,而是进行增量复制。

② Master 宕机

一 : 在Slave中执行SLAVEOF NO ONE 命令,断开主从关系并且提升为主库继续服务;

二 : 将主库重新启动后,执行 SLAVEOF命令,将其设置为其他Redis的从库,这个时候数据就同步回来了;

为什么主从库之间的复制不使用 AOF?

原因:

  • RDB 文件是二进制文件,写入磁盘和网络传输,IO 效率都比记录和传输 AOF 高。
  • 从库端进行恢复时,用 RDB 的恢复效率要高于用AOF。

哨兵模式

哨兵是Redis的一种运行模式,它专注于对Redis实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个Redis系统的可用性。

一般情况下,哨兵节点每隔10秒(故障转移时每隔1秒)向主从节点发送INFO命令,以此获取主从节点的信息。第一次执行时,哨兵仅知道我们给出的主节点信息,通过对主节点执行INFO命令就可以获取其从节点列表。如此周期性执行,就可以不断发现新加入的节点。
在这里插入图片描述
哨兵模式的监控配置信息,是通过配置从数据库的 sentinel monitor master-name ip redis-port quorum 来指定的,比如:

// mymaster 表示给master数据库定义了一个名字,后面的是master的ip和端口,1表示至少需要一个Sentinel进程同意才能将master判断为失效,如果不满足这个条件,则自动故障转移(failover)不会执行
sentinel monitor mymaster 127.0.0.1 6379 1

哨兵节点通信

当哨兵启动后,会与master建立一条连接,用于订阅mastersentinel:hello 频道。

该频道用于获取监控该master的其它哨兵的信息。并且还会建立一条定时向master发送INFO命令获取master信息的连接。

「当哨兵与master建立连接后,定期会向(10秒一次)master和slave发送INFO命令,若是master被标记为主观下线,频率就会变为1秒一次。」

并且,定期向 sentinel:hello 频道发送自己的信息,以便其它的哨兵能够订阅获取自己的信息。

以及,「定期的向master、slave和其它哨兵发送PING命令(每秒一次),以便检测对象是否存活」,若是对方接收到了PING命令,无故障情况下,会回复PONG命令。

所以,哨兵通过建立这两条连接、通过定期发送INFO、PING命令来实现哨兵与哨兵、哨兵与master之间的通信。

主观下线和客观下线

  • 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个、PING 命令。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值,则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
  • 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有
    Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
  • 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
  • 在一般情况下, 每个Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
  • 若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除

选举规则

当master被认为客观下线后,又是怎么进行故障恢复的呢?原来哨兵中首先选举出一个老大哨兵来进行故障恢复,选举老大哨兵的算法叫做「Raft算法」:

  • 发现master下线的哨兵(sentinelA)会向其它的哨兵发送命令进行拉票,要求选择自己为哨兵大佬。
  • 若是目标哨兵没有选择其它的哨兵,就会选择该哨兵(sentinelA)为大佬。
  • 若是选择sentinelA的哨兵超过半数(半数原则),该大佬非sentinelA莫属。
  • 如果有多个哨兵同时竞选,并且可能存在票数一致的情况,就会等待下次的一个随机时间再次发起竞选请求,进行新的一轮投票,直到大佬被选出来。

选出大佬哨兵后,大佬哨兵就会对故障进行自动回复,从slave中选出一名slave作为主数据库,选举的规则如下所示:

  1. 所有的slave中slave-priority优先级最高的会被选中。
  2. 若是优先级相同,会选择偏移量最大的,因为偏移量记录着数据的复制的增量,越大表示数据越完整。
  3. 若是以上两者都相同,选择ID最小的。

通过以上的层层筛选最终实现故障恢复,当选的slave晋升为master,其它的slave会向新的master复制数据,若是down掉的master重新上线,会被当作slave角色运行。

哨兵模式搭建

同时创建一个文件夹用于存放三个哨兵的配置文件:

mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel1.conf 
mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel2.conf 
mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel3.conf

分别在这三个文件中添加如下配置:

daemonize yes # 在后台运行
sentinel monitor mymaster 127.0.0.1 6379 1 # 给master起一个名字mymaster,并且配置master的ip和端口
sentinel auth-pass mymaster 123456 # master的密码
port 26379 #另外两个配置36379,46379端口
sentinel down-after-milliseconds mymaster 3000 # 3s未回复PING就认为master主观下线
sentinel parallel-syncs mymaster 2  # 执行故障转移时,最多可以有2个slave实例在同步新的master实例
sentinel failover-timeout mymaster 100000 # 如果在10s内未能完成故障转移操作认为故障转移失败

配置完后分别启动三台哨兵:

./redis-server sentinel1.conf --sentinel
./redis-server sentinel2.conf --sentinel
./redis-server sentinel3.conf --sentinel
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农BookSea

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值