redis高可用实现原理——主从,哨兵

前言

单节点系统有明显的缺点,一旦发生故障会导致服务不可用。而且,单个节点处理所有的请求,吞吐量有限,容量也有限

Redis实现高可用,通常有三种部署模式:主从哨兵集群

本文将介绍主从和哨兵模式的实现原理

主从

主从模式是常见的高可用手段,多个从节点能分担读请求压力,也能进行数据备份

2.8以前

分为同步和命令传播两个阶段

同步

  1. 从服务器向主服务器发送SYNC命令
  1. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
  1. 当主服务器的BGSAVE命令执行完毕时,主服务器会将生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
  1. 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态

命令传播

在同步操作执行完毕之后,主从服务器两者的数据库将达到一致状态,但这种一致并不是一成不变的,每当主服务器执行客户端发送的写命令时,主服务器的数据库就有可能会被修改,并导致主从服务器状态不再一致

因此,主服务器会将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态

问题

这种主从同步的方式问题在于断线后重复制

如果处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动重连接重新连上了主服务器,并继续复制主服务器

此时redis会再次生产rdb文件,发给从服务器。但其实这两个服务器保存的数据大部分都是相同的,并没有必要再完整地生成一次rdb文件

且生成rdb文件是一个非常低效的操作

  • 会耗费主服务器大量的CPU、内存和磁盘I/O资源
  • 将其发给从服务器,消耗大量网络带宽
  • 从服务器载入rdb的过程中,会因为阻塞而没办法处理命令请求

因此redis在2.8以后对此进行了优化

2.8以后

使用PSYNC(部分重同步)命令代替SYNC,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,而不是发送完成rdb文件

PSYNC原理

PSYNC是基于复制偏移量,和服务器运行ID来完成的

执行复制的双方——主服务器和从服务器会分别维护一个复制偏移量offset:

  • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
  • 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N

同时主服务器会维护一个复制积压缓冲区,是一个固定长度先进先出(FIFO)队列,默认大小为1MB

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面

当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器:

  • 如果offset偏移量之后的数据仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作
  • 如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作

同时也会考虑服务器运行ID,每个redis服务器都有自己的运行ID:

  • 当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来
  • 当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID
  • 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作。
  • 相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作

在这里插入图片描述

复制积压缓冲区的大小

复制积压缓冲区的大小可以根据公式second*write_size_per_second进行调整:

  • second:从服务器断线后,重新连接上主服务器所需的平均时间
  • write_size_per_second:主服务器平均每秒产生的写命令数据量

安全起见可以留一定buffer,根据不同的业务做不同的调整,能更好地利用部分重同步的优势

心跳检测

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送心跳,包含从服务器当前的复制偏移量

心跳有以下3个作用:

  1. 检测从节点的网络状态:越久没有给主节点发送心跳,从节点网络越差
  1. 检测命令丢失:如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送心跳时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,重新发给从服务器
  1. 辅助实现min-slaves:redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。例如配置min-slaves-max-lag等于10,则如果从服务器的延迟大于等于10秒,主服务器将拒绝执行写命令,防止脑裂问题。而每个从节点的延迟,就可以根据心跳检测来计算

哨兵

sentinel(哨兵)是Redis的高可用解决方案:

由一个或多个sentinel实例组成的sentinel系统,可以监控多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求

启动

首先需首先需要给哨兵节点配置需要监控的主服务器地址,并启动

对于每个被sentinel监视的主服务器来说,sentinel会创建两个连向主服务器的异步网络连接:

  1. 命令连接,这个连接专门用于向主服务器发送命令,并接收命令回复
  1. 订阅连接,这个连接专门用于订阅主服务器的__sentinel__:hello频道

sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的以下信息:

  • 关于主服务器本身的信息
  • 主服务器属下所有从服务器的信息,因此sentinel无须用户提供从服务器的地址信息,就可以自动发现从服务器

sentinel同样也会和每个从服务器创建命令连接和订阅连接 默认每十秒一次通过命令连接向从服务器发送INFO命令,获取以下信息:

  • 主从服务器的连接状态
  • 从服务器的优先级
  • 从服务器的复制偏移量

订阅连接

sentinel既通过命令连接向服务器的__sentinel__:hello频道发送信息,包括了sentinel本身的信息

又通过订阅连接从服务器的__sentinel__:hello频道接收信息

因此,对于监控同一个服务器的多个sentinel来说,一个sentinel发送的信息会被其他sentinel接收到,这些信息会被用于更新其他Sentinel对发送信息sentinel的认知

所以用户在使用sentinel的时候并不需要提供各个sentinel的地址信息,监视同一个主服务器的多个sentinel可以自动发现对方

当sentinel通过频道信息发现一个新的sentinel时,会创建一个连向新sentinel的命令连接,而新sentinel也同样会创建连向这个sentinel的命令连接,最终监视同一主服务器的多个Sentinel将形成相互连接的网络

在这里插入图片描述

主观下线

sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线

sentinel配置文件中的down-after-milliseconds选项指定了sentinel判断实例进入主观下线所需的时间长度:如果一个实例在down-aftermilliseconds毫秒内,连续向sentinel返回无效回复,那么sentinel会修改这个实例所对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已经进入主观下线状态

为啥称作主观下线?

  • 因为可能多个sentinel配置的down-after-milliseconds不一样,其他sentinel还不认为该节点已经下线
  • 或者当前sentinel网络故障,造成了误判

因此被监控节点并不是真正的下线,还需要向其他sentinel进行确认

客观下线

为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态

当sentinel从其他是sentinel那里接收到足够数量(可以手动配置)的已下线判断之后,sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作

根据其他sentinel发回的is-master-down-by-addr命令回复,sentinel将统计其他sentinel同意主服务器已下线的数量,当这一数量达到配置指定的判断客观下线所需的数量时,sentinel会将主服务器

实例结构flags属性的SRI_O_DOWN标识打开,表示主服务器已经进入客观下线状态

选举领头sentinel

监视这个下线主服务器的各个sentinel会进行协商,选举出一个领头sentinel,并由领头sentinel对下

线主服务器执行故障转移操作

选举领头sentinel的方法是对Raft算法的实现,以下是选举规则:

  • 所有在线的Sentinel都有被选为领头Sentinel的资格
  • 每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元的值都会自增一次
  • 每个发现主服务器进入客观下线的sentinel都会要求其他sentinel将自己设置为局部领头sentinel
  • sentinel设置局部领头sentinel的规则是先到先得:最先向目标sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头sentinel,而之后接收到的所有设置要求都会被目标Sentinel拒绝
  • 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头sentinel,那么这个sentinel成为领头sentinel
  • 因为领头sentinel的产生需要半数以上sentinel的支持,并且每个sentinel在每个配置纪元里面只能设置一次局部领头sentinel,所以在一个配置纪元里面,只会出现一个领头sentinel
  • 如果在给定时限内,没有一个sentinel被选举为领头sentinel,那么各个sentinel将在一段时间之后再次进行选举,直到选出领头sentinel为止

选出新主

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

  1. 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器
  1. 让已下线主服务器属下的所有从服务器改为复制新的主服务器
  1. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧主服务器重新上线时,它就会成为新的主服务器的从服务器

新的主服务器是怎样挑选出来的?根据以下规则一条条过滤:

  • 删除列表中所有处于下线或者断线状态的从服务器,这可以保证列表中剩余的从服务器都是正常在线的
  • 删除列表中所有最近五秒内没有回复过领头Sentinel的INFO命令的从服务器,这可以保证列表中剩余的从服务器都是最近成功进行过通信的
  • 删除所有与已下线主服务器连接断开超过down-aftermilliseconds*10毫秒的从服务器,保证剩余的从服务器保存的数据都是比较新的
  • 之后,领头Sentinel将根据从服务器的优先级,对列表中剩余的从服务器进行排序,并选出其中优先级最高的从服务器
  • 如果有多个具有相同最高优先级的从服务器,那么领头sentinel将按照从服务器的复制偏移量,对具有相同最高优先级的所有从服务器进行排序,并选出其中偏移量最大的从服务器。复制偏移量最大的意味着保存了最新数据的从服务器
  • 最后,如果有多个优先级最高、复制偏移量最大的从服务器,那么领头sentinel将按照运行ID对这些从服务器进行排序,并选出其中运行ID最小的从服务器,这条规则是保证一定能选出一个主节点

故障转移

  • 领头sentinel向被选中的从服务器发送SLAVEOF no one命令
  • 领头sentinel会以每秒一次的频率(平时是每十秒一次),向被升级的从服务器发送INFO命令,并观察其role是否变为master,如果是,表明升级成功
  • 让已下线主服务器属下的所有从服务器去复制新的主服务器,通过向从服务器发送SLAVEOF 新主来实现
  • 旧主重新上线后,sentinel就会向它发送SLAVEOF 新主 的命令,让他成为从节点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值