likes
comments
collection
share

redis核心篇(三)-高可用架构(一)之主从与哨兵

作者站长头像
站长
· 阅读数 20

1.前言

2. 主从

在reids中,可以通过执行replicaof(Redis 5.0之前使用 slaveof)命令形成主库和从库的关系或者在配置文件配置replicaof选项,让一个服务器去复制另外一个服务器,被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(slave)

redis核心篇(三)-高可用架构(一)之主从与哨兵 主从之间的关系:

  • 主服务可以有多个从服务器,也可以没有从服务,通常一个redis服务什么都不配,默认就为master
  • 从服务器只能有一个master

2.1 主从关联

redis服务默认都为master,配置主从关系,可以通过配置主节点信息开启主从复制,有如下三种方式:

  • 配置文件:在从服务器的配置文件添加如下配置
replicaof <masterip> <masterport>
  • 启动参数:redis-server 启动命令后面加入如下参数: --replicaof masterip masterport
  • 客户端命令:连接上redis之后,执行命令:replicaof masterip masterport,则该 Redis 实例成为从节点

2.1.1 演示

2.2 主从数据一致性

主从关系,既然涉及多个节点,那么在多个节点之间,必然会涉及到节点与节点之间数据同步延迟问题,也就是数据一致性方案;那redis对于数据一致性的方案又是怎么权衡的呢?对于多个节点之间的数据应该怎么同步,直接上图,我大致画了三个方向的解法;有其它解法的小伙伴,可在评论去留言哦:

redis核心篇(三)-高可用架构(一)之主从与哨兵

  • 强一致性的解决方案:节点与节点之间的数据同步采用完全同步的方式,所有节点写完数据才给客户端响应。此种解法在高性能和高可靠的权衡上,偏向了可靠;虽然保证了数据可靠,但也影响了性能,特别是在多个从节点的时候尤为明显。
  • 强一致性的解决方案:节点与节点之间的数据同步完全采用异步,此种解法是在高性能和高可靠的权衡上,偏向于高性能,性能提高了,数据的可靠性得不到保障,易丢失数据。
  • 最终一致性的解决方案:master将消息丢入异步队列(或缓冲区),通过异步线程来消费队列(缓冲区)的数据,以此来同步节点之间的数据,在高性能和高可靠的权衡上互相做了个折中,其实这个也是来源于Base理论,BASE是指基本可用(Basically Available)、柔性状态(Soft State)、最终一致性(Eventual Consistency) 其实对于大多数开源产品,在高性能和高可靠的权衡上,并没有给出特定的一种解决方案,通常是把决定权交给使用者,像kafka的ack机制,通过设置是0还是1或者-1,使用者根据自己的业务场景,自己决定是要消息的高吞吐率还是消息的高可靠;但对于我们的redis来说,并没有提供如此灵活配置,其默认使用异步复制,弱一致性的解法,因为它追求的是极致的高性能。

2.3 主从同步原理

在redis的主从复制实现上,有两个不同的实现版本:2.8版之前使用的sync命令,2.8及2.8之后的版本,使用的psync命令,本篇,将分别介绍这两个命令的实现。

主从复制主要分为两个操作,数据同步和命令传播

  • 数据同步:主要将从服务器的状态同步更新至主服务器所在的状态。
  • 命令传播:主要作用于当主服务器所在状态被更改,导致主从数据不一致的情况时,让主从服务器的状态重回一致。 数据同步可分为2种情况:
  • 首次同步:从节点首次连接到master的数据同步
  • 断开重连:从节点断开后重新链接到master

2.3.1 sync

2.3.1.1 首次同步

主从库第一次复制过程大体可以分为 3 个阶段:1)连接建立阶段(即准备阶段);2)主库同步数据到从库阶段; 3)发送同步期间新写命令到从库;

redis核心篇(三)-高可用架构(一)之主从与哨兵

  • 链接建立阶段:该阶段的主要作用是在主从节点之间建立连接,为数据全量同步做好准备。从库会和主库建立连接的时候,从库执行slaveof并发送sync命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了。
  • 第二阶段:master执行bgsave命令生成RDB文件,并将文件发送给从库,同时主库为每一个 slave 开辟一块缓冲区记录从生成RDB文件开始收到的所有写命令;从库收到 RDB 文件后保存到磁盘,并清空当前数据库的数据,再加载 RDB 文件数据到内存中
  • 第三阶段:从节点加载RDB完成后,master将缓冲区的数据发送到从节点,Slave 接收并执行,从节点同步至主节点相同的状态。

通过上面的过程不难看出,当主从同步的时候,如果此时业务并发量很高,导致写命令qps飙升,很容器造成从节点在载入rdb文件过程中,缓冲区容量过小,导致主从同步失败,重新进行同步;容易形成bgsave和rdb重传操作的无限循环。

2.3.1.2 断开重连

在2.8版本之前,同步所用的sync命令,没有很好的处理断线重连的情况,当主从断开发生再次重连的时候;所执行的步骤同第一次主从同步过程,需要进行全量复制,过程同首次同步,此处就不再说明。

2.3.2 psync

在2.8版本,对于主从同步,redis采用了psync命令,psync的实现,主要是优化了sync主从断开之后,需要进行全量复制的弊端;通过增量复制来代替全量复制

增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效

2.3.2.1 复制偏移量,复制积压缓冲区,服务运行id

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量。
  • 主服务器的复制积压缓冲区(replication backlog)。
  • 服务器的运行ID(run ID)

直接上图,如下为主从命令传播过程:主服务器在进行命令传播的时候,它不仅会将写命令发送给所有的从服务器,还会将写命令存储到复制积压缓冲区。

redis核心篇(三)-高可用架构(一)之主从与哨兵

  • 主服务器的复制偏移量和从服务器的复制偏移量

    • 主服务器在每次向从服务器传播N个字节数据的时候,其复制偏移量的值则加N
    • 从服务在每次收到N个字节数据的时候,其复制偏移量的值则加N
  • 服务运行Id(runId),不管是主服务器还是从服务器都有自己的服务运行id,runId在服务器启动的时候自动生成,例如33b9b28ef80q2fdc9ab5e3fcbbbabff4dcdcedbg。

  • 复制积压缓冲区:

    • 复制积压缓冲区是由主服务维护的一个先进先出的队列,默认大小为1M,当队列满的时候,会自动弹出最初进入队列的元素
    • 复制积压缓冲区主要存储两种类型的数据,复制偏移量(offset)和复制偏移量对应的字节命令;当主服务器在进行命令传播的时候,不仅会将写命令发送给从服务器,还会将写命令以及对应的偏移量写入队列中。
2.3.2.2 增量同步实现原理

在了解服务器运行ID、复制偏移量、复制积压缓冲区这些东西之后,再来看看psync是怎么实现增量同步的,直接上图,如下: redis核心篇(三)-高可用架构(一)之主从与哨兵

  • 当从服务器断开之后,再次收到复制命令指令的时候,此时向主服务发送psync命令会带上次maser的runId和上次复制的偏移量。
  • 当主服务器收到psync指令的时候,会校验runId与自身运行id是否一致,如果一致,则再校验offset之后的指令数据是否还存在复制积压缓冲区里,如果在,就返回增量同步结果,否则,返回全量同步结果;当时增量同步的时候,从节点只同步复制积压缓冲区的命令即可,主节点也无需生成rdb文件给从节点重新载入。

3.哨兵

主从架构虽然可以用于读写分离,减轻单机读的压力,但是当主挂了之后,主从架构无法自动选举出新的master,整个主从无法再提供写能力,所以对于高可用的系统架构模型,主从还远远不够的;必须要有一个高可用的方案,在主服务器挂了之后,在一定条件之内,可以将原先的从服务器选举出一台当做master,使整个redis集群仍然能向外提供读写服务。

3.1 什么是哨兵

redis核心篇(三)-高可用架构(一)之主从与哨兵

哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例的运行状态进行监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性;总结来说,哨兵具有的功能有如下几个:

  • 监控(monitor):持续监控 master 、slave 是否处于预期工作状态
  • 下线:
    • 客观下线
    • 主观下线
  • 故障转移

3.2 哨兵功能

3.2.1 监控

默认情况下,哨兵以每秒一次的频率向所有与它创建了命令链接的实例(主服务器,从服务器,其它sentinel服务节点)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线或者存活

实例对PING命令的回复可分为两种情况:

  • 有效回复:实例返回+PONG、-LOADING、-MASTERDOWN三种回复的其中一种。
  • 无效回复:实例返回除+PONG、-LOADING、-MASTERDOWN三种回复之外的其他回复,或者在指定时限内没有返回任何回复

无效回复的时间配置:sentinel配置文件中的down-after-milliseconds选项指定了sentinel判断实例进入主观下线所需的时间长度,如果一个实例在down-after-milliseconds毫秒内,向Sentinel返回的都是无效回复,则哨兵会将该实例标记为主观下线状态。

3.2.2 下线

3.2.2.1 主观下线

哨兵节点利用PING命令来检测与其建立链接的实例是否存活,如果PING命令的回复是无效回复,哨兵节点则会标记该实例为主观下线。

仅仅靠主观下线就判断对应实例是否存活,存在很大的误判性;通过监控一节的介绍可知,对PING命令的回复是无效回复还是有效回复的影响因素有很多,如不同哨兵的负载情况,哨兵节点所在的网络情况,各自的down-after-milliseconds配置。

基于主观下线的话,各个不同的哨兵节点的主观性太强,对于被监控的实例,如果实例的role(info命令查询)是slave,则可直接通过主观下线来判断该实例是否存活,如果实例的role是master,则无法通过单个哨兵节点的主观下线行为来判断该实例是否存活,需要结合更多哨兵的监控结果来判断,这样才能降低误判率

3.2.2.2 客观下线

判断master是否下线不能只由一个哨兵说了算;当判断该master主观下线的哨兵节点数量达到quorum(可配置)个时,才能将master标记为客观下线,也就是说这是一个客观存在的事实;

这里的quorum是一个可配置的参数,由哨兵的配置决定;如下,为哨兵节点的 一段配置文件:

# sentinel monitor <master-name> <master-host> <master-port> <quorum>

sentinel monitor mymaster 127.0.0.1 6379 2

这条配置项用于告知哨兵需要监听的主节点:

  • sentinel monitor:代表监控
  • mymaster:代表主节点的名字
  • 127.0.0.1 和6379 分别代表主节点的名字和端口号
  • 2: 表示quorum,代表只有两个或两个以上的哨兵认为主节点不可用的时候,才会把 master 设置为客观下线状态

对于监视同一个master节点的不同sentinel来说,它们将master判断为客观下线的条件可能也不同:当一个sentine判断master为客观下线时,其它sentinel可能并不是那么认为。比如,另外一个sentinel的节点quorum配置的值可能是5.

过半机制:当有 N 个哨兵实例时,要有 N/2 + 1 个实例判断 master 为「主观下线」,才能最终判定 Master 为「客观下线」

3.2.3 自动切换主从

3.2.3.1 选举领头sentinel

当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个sentinel会进行协商,选举出一个领头entinel,并由领头sentinel对下线主服务器执行故障转移操作。

在了解选举领头sentinel之前,先记住一个关键词,配置纪元:

配置纪元(configuration epoch):配置纪元就是一个计数器,每一次的sentinel选举,配置纪元的值都会加1;在一个配置纪元值里面,每一个sentinel有且仅有一次机会,将某个sentineL设置为leader。

为了选举出领头的sentinel,sentinel集群的节点,会互相发送is-master-down-by-addr命令,该命令会带上自己的runId,这表示希望将自己设置成局部领头sentinel,直接上图,如下所示:

redis核心篇(三)-高可用架构(一)之主从与哨兵

  • sentinel设置局部领头sentinel的规则是先到先得:既最先向目标sentinel发送设置要求的源sentinel将成为目标sentinel的局部领头sentinel,而之后接收到的所有设置要求都会被目标sentinel拒绝
  • 目标sentinel的回复包含leader_runId参数和leader_epoch,分表表示目标sentinel的局部领头运行id和配置纪元
  • 源sentinel收到命令回复后,会检查leader_epoch的值是否与自己的相同,如果相同的话,那么源sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel
  • 如果有某个sentinel被半数以上的sentinel设置成了局部领头sentinel,那么这个sentinel成为领头sentinel;在给定时限内,没有一个sentinel被选举为领头sentinel,那么各个sentinel将在一段时间之后再次进行选举,直到选出领头sentinel为止
3.2.3.2 故障转移

选举产生出领头Sentinel之后,领头Sentinel将对已下线的主服务器执行故障转移操作,故障转移主要包含三个步骤:

  • 选举master:挑选出一个从服务器,并将其转换为主服务器
  • 让已下线主服务器属下的所有从服务器改为复制新的主服务器
  • 如果原下线的主服务器重新上线,使之成为新的master节点的从服务器
3.2.3.2.1 选出新的主服务器

为了挑选出一个状态良好、数据完整的从服务器,领头的sentinel会通过一定规则来进行刷选(在刷选之前,领头sentinel会将原master的从节点保存为一个列表):

  • 根据节点在线状态:下线的从节点直接从列表剔除
  • 根据节点网络状态:从列表剔除所有与已下线主服务器连接断开超过down-after-milliseconds*10毫秒的从服务器;如果从库与主库连接断开时间超过阀值,则说明,这个从节点的网络情况并不是很好
3.2.3.2.2 通知

当新的主服务器出现之后,领头sentinel下一步要做的就是,让已下线主服务器属下的所有从服务器去复制新的主服务器,这一动作其实就是进行主从关联操作,详细过程,可参考前面的主从一节。

3.3 总结

  • 主从切换,并不是随意选择一个哨兵就可以执行,而是通过投票仲裁,选择一个 Leader,由这个 Leader 负责主从切换
  • sentinel以每秒一次的频率向实例(包括主服务器、从服务器、其他sentinel)发送PING命令,并根据实例对PING命令的回复来判断实例是否在线,当一个实例在指定的时长中连续向sentinel发送无效回复时,sentinel会将这个实例判断为主观下线
转载自:https://juejin.cn/post/7051129092908777503
评论
请登录