Redis(五)——主从复制、哨兵

iwehdio的博客园:https://www.cnblogs.com/iwehdio/

学习自:

1、主从架构

  • Redis也跟关系型数据(MySQL)一样,如果有过多请求还是撑不住的。

  • 因为Redis如果只有一台服务器的话,那随着请求越来越多:

    • Redis的内存是有限的,可能放不下那么多的数据
    • 单台Redis支持的并发量也是有限的
    • 万一这台Redis挂了,所有的请求全走关系数据库了,那就更炸了。
  • 显然,出现的上述问题是因为一台Redis服务器不够,所以多搞几台Redis服务器就可以了。为了实现我们服务的高可用性,可以将这几台Redis服务器做成是主从来进行管理。

  • Redis的主从架构特点:

    • 服务器负责接收请求
    • 服务器负责接收请求
    • 从服务器的数据由主服务器复制过去。主从服务器的数据是一致

    image-20210205192633526

  • 主从架构的好处

    • 读写分离(主服务器负责写,从服务器负责读)
    • 高可用(某一台从服务器挂了,其他从服务器还能继续接收请求,不影响服务)
    • 处理更多的并发量(每台从服务器都可以接收读请求,读QPS就上去了)
  • 主从架构的特点之一:主服务器和从服务器的数据是一致的。

  • 在Redis中,用户可以通过执行SALVEOF命令或者设置salveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(salve)

  • 复制功能分为两个操作:

    • 同步(sync)

      • 将从服务器的数据库状态更新至主服务器的数据库状态
    • 命令传播(command propagate)

      • 主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致,让主从服务器的数据库状态重新回到一致状态
  • 从服务器对主服务器的同步又可以分为两种情况

    • 初次同步:从服务器没有复制过任何的主服务器,或者从服务器要复制的主服务器跟上次复制的主服务器不一样
    • 断线后同步:处于命令传播阶段的主从服务器因为网络原因中断了复制,从服务器通过自动重连重新连接主服务器,并继续复制主服务器
  • 在Redis2.8以前,断线后复制这部分其实缺少的只是部分的数据,但是要让主从服务器重新执行SYNC命令,这样的做法是非常低效的。(因为执行SYNC命令是把所有的数据再次同步,而不是只同步丢失的数据)。

  • Redis从2.8版本开始,使用PSYNC命令来替代SYNC命令执行复制时同步的操作。PSYNC命令具有完整重同步和部分重同步两种模式(其实就跟上面所说的初次复制和断线后复制差不多个意思)。

  • 复制的前置的工作

    • 从服务器设置主服务器的IP和端口
    • 建立与主服务器的Socket连接
    • 发送PING命令(检测Socket读写是否正常与主服务器的通信状况)
    • 身份验证(看有没有设置对应的验证配置)
    • 从服务器给主服务器发送端口的信息,主服务器记录监听的端口
  • 完整重同步是怎么实现的:

    • 从服务器向主服务器发送PSYNC命令
    • 收到PSYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件。并用一个缓冲区来记录从现在开始执行的所有写命令
    • 当主服务器的BGSAVE命令执行完后,将生成的RDB文件发送给从服务器,从服务器接收和载入RBD文件。将自己的数据库状态更新至与主服务器执行BGSAVE命令时的状态。
    • 主服务器将所有缓冲区的写命令发送给从服务器,从服务器执行这些写命令,达到数据最终一致性。
  • 部分重同步,可以让我们断线后重连只需要同步缺失的数据。部分重同步功能由以下部分组成:

    • 主从服务器的复制偏移量
    • 主服务器的复制积压缓冲区
    • 服务器运行的ID(run ID)
  • 复制偏移量:执行复制的双方都会分别维护一个复制偏移量

    • 主服务器每次传播N个字节,就将自己的复制偏移量加上N
    • 从服务器每次收到主服务器的N个字节,就将自己的复制偏移量加上N
    • 通过对比主从复制的偏移量,就很容易知道主从服务器的数据是否处于一致性的状态!
  • 断线重连以后,从服务器向主服务器发送PSYNC命令,报告现在的偏移量是36,那么主服务器该对从服务器执行完整重同步还是部分重同步呢??这就交由复制积压缓冲区来决定。

    • 当主服务器进行命令传播时,不仅仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面(这个大小可以调的)。如果复制积压缓冲区存在丢失的偏移量的数据,那就执行部分重同步,否则执行完整重同步。
  • 服务器运行的ID(run ID)实际上就是用来比对ID是否相同。如果不相同,则说明从服务器断线之前复制的主服务器和当前连接的主服务器是两台服务器,这就会进行完整重同步。

  • 当完成了同步之后,主从服务器就会进入命令传播阶段。这时主服务器只要将自己的写命令发送给从服务器,而从服务器接收并执行主服务器发送过来的写命令,就可以保证主从服务器一直保持数据一致了!

    • 在命令传播阶段,从服务器默认会以每秒一次的频率,向服务器发送命令REPLCONF ACK <replication_offset> 其中replication_offset是从服务器当前的复制偏移量
    • 发送这个命令主要有三个作用:
      • 检测主从服务器的网络状态
      • 辅助实现min-slaves选项
      • 检测命令丢失

2、哨兵机制

  • 如果从服务器挂了,没关系,我们一般会有多个从服务器,其他的请求可以交由没有挂的从服务器继续处理。如果主服务器挂了,怎么办?因为我们的写请求由主服务器处理,只有一台主服务器,怎么办呢 ?

  • Redis提供了哨兵(Sentinel)机制供我们解决上面的情况。如果主服务器挂了,我们可以将从服务器升级为主服务器,等到旧的主服务器(挂掉的那个)重连上来,会将它(挂掉的主服务器)变成从服务器。

    • 这个过程叫做主备切换(故障转移)
    • 正常的情况下,哨兵(Sentinel)监视这所有的服务器。
    • 主服务器挂了,主从复制操作就中止了,并且哨兵系统是可以察觉出主服务挂了。
    • Redis提供哨兵机制可以将选举一台从服务器变成主服务器。
    • 然后旧的主服务器如果重连了,会变成从服务器。

    image-20210205192723657

  • 哨兵(Sentinel)机制主要用于实现Redis的高可用性,主要的功能如下:

    • 哨兵不停地监控Redis主从服务器是否正常工作。
    • 如果某个Redis实例有故障,那么哨兵负责发送消息通知管理员。
    • 如果主服务器挂掉了,会自动将从服务器提升为主服务器(包括配置都会修改)。
    • 哨兵可以作为配置中心,能够提供当前主服务器的信息。
  • Sentinel可以让我们的Redis实现高可用,Sentinel作为这么一个组件,自身也必然是高可用的(不可能是单点的)。

  • Sentinel本质上只是一个运行在特殊模式下的Redis服务器。因为Sentinel做的事情和Redis服务器是不一样的,所以它们的初始化是有所区别的(比如,Sentinel在初始化的时候并不会载入AOF/RDB文件,因为Sentinel根本就不用数据库)。

  • 启动和初始化哨兵:

    • 在启动的时候会将普通Redis服务器的代码替换成Sentinel专用代码。(所以Sentinel虽然作为Redis服务器,但是它不能执行SET、DBSIZE等等命令,因为命令表的代码被替换了)
    • 接着,初始化Sentinel的状态,并根据给定的配置文件初始化Sentinel监视的主服务器列表
    • 最后,Sentinel会创建两个连向主服务器的网络连接
      • 命令连接(发送和接收命令)
      • 订阅连接(订阅主服务器的sentinel:hello频道)
  • 获取和更新信息:

    • Sentinel通过主服务器发送INFO命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构,以及命令连接和订阅连接。
    • 当发现有新的从服务器出现时,除了创建对应的从服务器实例结构,Sentinel还会创建命令连接和订阅连接。
    • 在Sentinel运行的过程中,通过命令连接会以每两秒一次的频率向监视的主从服务器_sentinel_:hello频道发送命令(主要发送Sentinel本身的信息,监听主从服务器的信息),并通过订阅连接接收_sentinel_:hello频道的信息。
      • 这样一来一回,我们就可以更新每个Sentinel实例结构的信息
  • 判断主服务器是否下线有两种情况:

    • 主观下线

      • Sentinel会以每秒一次的频率向与它创建命令连接的实例(包括主从服务器和其他的Sentinel)发送PING命令,通过PING命令返回的信息判断实例是否在线
      • 如果一个主服务器在down-after-milliseconds毫秒内连续向Sentinel发送无效回复,那么当前Sentinel就会主观认为该主服务器已经下线了。
    • 客观下线

      • 当Sentinel将一个主服务器判断为主观下线以后,为了确认该主服务器是否真的下线,它会向同样监视该主服务器的Sentinel询问,看它们是否也认为该主服务器是否下线。
      • 如果足够多的Sentinel认为该主服务器是下线的,那么就判定该主服务为客观下线,并对主服务器执行故障转移操作
    • 在多少毫秒内无效回复才认定主服务器是主观下线的,以及多少个Sentinel认为主服务器是下线的,才认定为客观下线。这都是可以配置

  • 选举领头Sentinel和故障转移:

    • 当一个主服务器认为为客观下线以后,监视这个下线的主服务器的各种Sentinel会进行协商,选举出一个领头的Sentinel,领头的Sentinel会对下线的主服务器执行故障转移操作。
    • 选举领头Sentinel的规则也比较多,总的来说就是先到先得(哪个快,就选哪个)
    • 在已下线主服务器属下的从服务器中,挑选一个转换为主服务器
    • 让已下线主服务器属下的所有从服务器改为复制新的主服务器
    • 已下线的主服务器重新连接时,让他成为新的主服务器的从服务器
    • 挑选某一个从服务器作为主服务器也是有策略的,大概如下:
      • (1)跟master断开连接的时长
      • (2)slave优先级
      • (3)复制offset
      • (4)run id
  • 目前为止的主从+哨兵架构可以说Redis是高可用的,但要清楚的是:Redis还是会丢失数据的:

    • 异步复制导致的数据丢失

      • 有部分数据还没复制到从服务器,主服务器就宕机了,此时这些部分数据就丢失了
    • 脑裂导致的数据丢失

      • 有时候主服务器脱离了正常网络,跟其他从服务器不能连接。此时哨兵可能就会认为主服务器下线了(然后开启选举,将某个从服务器切换成了主服务器),但是实际上主服务器还运行着。这个时候,集群里就会有两个服务器(也就是所谓的脑裂)。
      • 虽然某个从服务器被切换成了主服务器,但是可能客户端还没来得及切换到新的主服务器,客户端还继续写向旧主服务器写数据。旧的服务器重新连接时,会作为从服务器复制新的主服务器(这意味着旧数据丢失)。

iwehdio的博客园:https://www.cnblogs.com/iwehdio/
来源与结束于否定之否定。
原文地址:https://www.cnblogs.com/iwehdio/p/14379476.html