Redis之Sentinel(哨兵)详述

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 主从复制奠定了Redis分布式的基础,但是普通的主从复制并不能达到高可用的状态。在普通的主从复制模式下,如果主服务器宕机,就只能通过运维人员手动切换主服务器,很显然这种方案并不可取。针对上述情况,Redis官方推出了可抵抗节点故障的高可用方案——Redis Sentinel(哨兵)。Redis Sentinel(哨兵):由一个或多个Sentinel实例组成的Sentinel系统,它可以监视任意多个主从服务器,当监视的主服务器宕机时,自动下线主服务器,并且择优选取从服务器升级为新的主服务器。

1、简介

主从复制奠定了Redis分布式的基础,但是普通的主从复制并不能达到高可用的状态。在普通的主从复制模式下,如果主服务器宕机,就只能通过运维人员手动切换主服务器,很显然这种方案并不可取。

针对上述情况,Redis官方推出了可抵抗节点故障的高可用方案——Redis Sentinel(哨兵)。Redis Sentinel(哨兵):由一个或多个Sentinel实例组成的Sentinel系统,它可以监视任意多个主从服务器,当监视的主服务器宕机时,自动下线主服务器,并且择优选取从服务器升级为新的主服务器。

如下示例:当旧Master下线时长超过用户设定的下线时长上限,Sentinel系统就会对旧Master执行故障转移操作,故障转移操作包含三个步骤:

  1. 在Slave中选择数据最新的作为新的Master
  2. 向其他Slave发送新的复制指令,让其他从服务器成为新的Master的Slave
  3. 继续监视旧Master,如果其上线则将旧Master设置为新Master的Slave

本文基于如下资源清单进行开展:

IP地址

节点角色

端口

192.168.211.104

Redis Master/ Sentinel

6379/26379

192.168.211.105

Redis Slave/ Sentinel

6379/26379

192.168.211.106

Redis Slave/ Sentinel

6379/26379


2、Sentinel初始化与网络连接

Sentinel并没有什么特别神奇的地方,它就是一个更加简单的Redis服务器,在Sentinel启动的时候它会加载不同的命令表和配置文件,因此从本质上来讲Sentinel就是一个拥有较少命令和部分特殊功能的Redis服务。当一个Sentinel启动时它需要经历如下步骤:

  1. 初始化Sentinel服务器
  2. 替换普通Redis代码为Sentinel的专用代码
  3. 初始化Sentinel状态
  4. 根据用户给定的Sentinel配置文件,初始化Sentinel监视的主服务器列表
  5. 创建连接主服务器的网络连接
  6. 根据主服务获取从服务器信息,创建连接从服务器的网络连接
  7. 根据发布/订阅获取Sentinel信息,创建Sentinel之间的网络连接

2.1 初始化Sentinel服务器

Sentinel本质上就是一个Redis服务器,因此启动Sentinel需要启动一个Redis服务器,但是Sentinel并不需要读取RDB/AOF文件来还原数据状态。

2.2 替换普通Redis代码为Sentinel的专用代码

Sentinel用于较少的Redis命令,大部分命令在Sentinel客户端都不支持,并且Sentinel拥有一些特殊的功能,这些需要Sentinel在启动时将Redis服务器使用的代码替换为Sentinel的专用代码。在此期间Sentinel会载入与普通Redis服务器不同的命令表。

Sentinel不支持SET、DBSIZE等命令;保留支持PING、PSUBSCRIBE、SUBSCRIBE、UNSUBSCRIBE、INFO等指令;这些指令在Sentinel工作中提供了保障。

2.3 初始化Sentinel状态

装载Sentinel的特有代码之后,Sentinel会初始化sentinelState结构,该结构用于存储Sentinel相关的状态信息,其中最重要的就是masters字典。

struct sentinelState {

 

   //当前纪元,故障转移使用

uint64_t current_epoch;

 

   // Sentinel监视的主服务器信息

   // key -> 主服务器名称

   // value -> 指向sentinelRedisInstance指针

   dict *masters;

   // ...

} sentinel;

2.4 初始化Sentinel监视的主服务器列表

Sentinel监视的主服务器列表保存在sentinelState的masters字典中,当sentinelState创建之后,开始对Sentinel监视的主服务器列表进行初始化。

  • masters的key是主服务的名字
  • masters的value是一个指向sentinelRedisInstance指针

主服务器的名字由我们sentinel.conf配置文件指定,如下主服务器名字为redis-master(我这里是一主二从的配置):

daemonize yes

port 26379

protected-mode no

dir "/usr/local/soft/redis-6.2.4/sentinel-tmp"

sentinel monitor redis-master 192.168.211.104 6379 2

sentinel down-after-milliseconds redis-master 30000

sentinel failover-timeout redis-master 180000

sentinel parallel-syncs redis-master 1

sentinelRedisInstance实例保存了Redis服务器的信息(主服务器、从服务器、Sentinel信息都保存在这个实例中)。

typedef struct sentinelRedisInstance {

 

   // 标识值,标识当前实例的类型和状态。如SRI_MASTER、SRI_SLVAE、SRI_SENTINEL

   int flags;

   

   // 实例名称 主服务器为用户配置实例名称、从服务器和Sentinel为ip:port

   char *name;

   

   // 服务器运行ID

   char *runid;

   

   //配置纪元,故障转移使用

uint64_t config_epoch;

   

   // 实例地址

   sentinelAddr *addr;

   

   // 实例判断为主观下线的时长 sentinel down-after-milliseconds redis-master 30000

   mstime_t down_after_period;

   

   // 实例判断为客观下线所需支持的投票数 sentinel monitor redis-master 192.168.211.104 6379 2

   int quorum;

   

   // 执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量 sentinel parallel-syncs redis-master 1

   int parallel-syncs;

   

   // 刷新故障迁移状态的最大时限 sentinel failover-timeout redis-master 180000

mstime_t failover_timeout;

   

   // ...

} sentinelRedisInstance;

根据上面的一主二从配置将会得到如下实例结构:

2.5 创建连接主服务器的网络连接

当实例结构初始化完成之后,Sentinel将会开始创建连接Master的网络连接,这一步Sentinel将成为Master的客户端。

Sentinel和Master之间会创建一个命令连接和一个订阅连接:

  • 命令连接用于获取主从信息
  • 订阅连接用于Sentinel之间进行信息广播,每个Sentinel和自己监视的主从服务器之间会订阅_sentinel_:hello频道(注意Sentinel之间不会创建订阅连接,它们通过订阅_sentinel_:hello频道来获取其他Sentinel的初始信息)

Sentinel在创建命令连接完成之后,每隔10秒钟向Master发送一次INFO指令,通过Master的回复信息可以获得两方面的知识:

  • Master本身的信息
  • Master下的Slave信息

2.6 创建连接从服务器的网络连接

根据主服务获取从服务器信息,Sentinel可以创建到Slave的网络连接,Sentinel和Slave之间也会创建命令连接和订阅连接。

当Sentinel和Slave之间创建网络连接之后,Sentinel成为了Slave的客户端,Sentinel也会每隔10秒钟通过INFO指令请求Slave获取服务器信息。

到这一步Sentinel获取到了Master和Slave的相关服务器数据。这其中比较重要的信息如下:

  • 服务器ip和port
  • 服务器运行id run id
  • 服务器角色role
  • 服务器连接状态mater_link_status
  • Slave复制偏移量slave_repl_offset(故障转移中选举新的Master需要使用)
  • Slave优先级slave_priority

此时实例结构信息如下所示:

2.7 创建Sentinel之间的网络连接

此时是不是还有疑问,Sentinel之间是怎么互相发现对方并且相互通信的,这个就和上面Sentinel与自己监视的主从之间订阅_sentinel_:hello频道有关了。

Sentinel会与自己监视的所有Master和Slave之间订阅_sentinel_:hello频道,并且Sentinel每隔2秒钟向_sentinel_:hello频道发送一条消息,消息内容如下:

PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_ip>,<m_port>,<m_runid>,<m_epoch>"

其中s代码Sentinel,m代表Master;ip表示IP地址,port表示端口、runid表示运行id、epoch表示配置纪元。

多个Sentinel在配置文件中会配置相同的主服务器ip和端口信息,因此多个Sentinel均会订阅_sentinel_:hello频道,通过频道接收到的信息就可获取到其他Sentinel的ip和port,其中有如下两点需要注意:

  • 如果获取到的runid与Sentinel自己的runid相同,说明消息是自己发布的,直接丢弃
  • 如果不相同,则说明接收到的消息是其他Sentinel发布的,此时需要根据ip和port去更新或新增Sentinel实例数据

Sentinel之间不会创建订阅连接,它们只会创建命令连接:

此时实例结构信息如下所示:

3、Sentinel工作

Sentinel最主要的工作就是监视Redis服务器,当Master实例超出预设的时限后切换新的Master实例。这其中有很多细节工作,大致分为检测Master是否主观下线、检测Master是否客观下线、选举领头Sentinel、故障转移四个步骤。

3.1 检测Master是否主观下线

Sentinel每隔1秒钟,向sentinelRedisInstance实例中的所有Master、Slave、Sentinel发送PING命令,通过其他服务器的回复来判断其是否仍然在线。

sentinel down-after-milliseconds redis-master 30000

在Sentinel的配置文件中,当Sentinel PING的实例在连续down-after-milliseconds配置的时间内返回无效命令,则当前Sentinel认为其主观下线。Sentinel的配置文件中配置的down-after-milliseconds将会对其sentinelRedisInstance实例中的所有Master、Slave、Sentinel都适应。

无效指令指的是+PONG、-LOADING、-MASTERDOWN之外的其他指令,包括无响应

如果当前Sentinel检测到Master处于主观下线状态,那么它将会修改其sentinelRedisInstance的flags为SRI_S_DOWN

3.2 检测Master是否客观下线

当前Sentinel认为其下线只能处于主观下线状态,要想判断当前Master是否客观下线,还需要询问其他Sentinel,并且所有认为Master主观下线或者客观下线的总和需要达到quorum配置的值,当前Sentinel才会将Master标志为客观下线。

当前Sentinel向sentinelRedisInstance实例中的其他Sentinel发送如下命令:

SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>

  • ip:被判断为主观下线的Master的IP地址
  • port:被判断为主观下线的Master的端口
  • current_epoch:当前sentinel的配置纪元
  • runid:当前sentinel的运行id,runid

current_epoch和runid均用于Sentinel的选举,Master下线之后,需要选举一个领头Sentinel来选举一个新的Master,current_epoch和runid在其中发挥着重要作用,这个后续讲解。

接收到命令的Sentinel,会根据命令中的参数检查主服务器是否下线,检查完成后会返回如下三个参数:

  • down_state:检查结果1代表已下线、0代表未下线
  • leader_runid:返回*代表判断是否下线,返回runid代表选举领头Sentinel
  • leader_epoch:当leader_runid返回runid时,配置纪元会有值,否则一直返回0
  1. 当Sentinel检测到Master处于主观下线时,询问其他Sentinel时会发送current_epoch和runid,此时current_epoch=0,runid=*
  2. 接收到命令的Sentinel返回其判断Master是否下线时down_state = 1/0,leader_runid = *,leader_epoch=0

3.3 选举领头Sentinel

down_state返回1,证明接收is-master-down-by-addr命令的Sentinel认为该Master也主观下线了,如果down_state返回1的数量(包括本身)大于等于quorum(配置文件中配置的值),那么Master正式被当前Sentinel标记为客观下线。

此时,Sentinel会再次发送如下指令:

SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>

此时的runid将不再是0,而是Sentinel自己的运行id(runid)的值,表示当前Sentinel希望接收到is-master-down-by-addr命令的其他Sentinel将其设置为领头Sentinel。这个设置是先到先得的,Sentinel先接收到谁的设置请求,就将谁设置为领头Sentinel。

发送命令的Sentinel会根据其他Sentinel回复的结果来判断自己是否被该Sentinel设置为领头Sentinel,如果Sentinel被其他Sentinel设置为领头Sentinel的数量超过半数Sentinel(这个数量在sentinelRedisInstance的sentinel字典中可以获取),那么Sentinel会认为自己已经成为领头Sentinel,并开始后续故障转移工作(由于需要半数,且每个Sentinel只会设置一个领头Sentinel,那么只会出现一个领头Sentinel,如果没有一个达到领头Sentinel的要求,Sentinel将会重新选举直到领头Sentinel产生为止)。

3.4 故障转移

故障转移将会交给领头sentinel全权负责,领头sentinel需要做如下事情:

  1. 从原先master的slave中,选择最佳的slave作为新的master
  2. 让其他slave成为新的master的slave
  3. 继续监听旧master,如果其上线,则将其设置为新的master的slave

这其中最难的一步是如果选择最佳的新Master,领头Sentinel会做如下清洗和排序工作:

  1. 判断slave是否有下线的,如果有从slave列表中移除
  2. 删除5秒内未响应sentinel的INFO命令的slave
  3. 删除与下线主服务器断线时间超过down_after_milliseconds * 10 的所有从服务器
  4. 根据slave优先级slave_priority,选择优先级最高的slave作为新master
  5. 如果优先级相同,根据slave复制偏移量slave_repl_offset,选择偏移量最大的slave作为新master
  6. 如果偏移量相同,根据slave服务器运行id run id排序,选择run id最小的slave作为新master

新的Master产生后,领头sentinel会向已下线主服务器的其他从服务器(不包括新Master)发送SLAVEOF ip port命令,使其成为新master的slave。

到这里Sentinel的的工作流程就算是结束了,如果新master下线,则循环流程即可!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL Redis Sentinel
【怒怼大厂面试官】听说你精通Redis?说说Redis哨兵
面试官:Redis哨兵知道吧?知道的,Sentinel哨兵本质是一个运行在特殊模式下的Redis服务器。面试官:嗯然后呢?它的主要作用是通过检测Redis主从服务器的下线状态,选举出新Redis主服务器,也就是故障转移,来保证Redis的高可用性。
78 4
【怒怼大厂面试官】听说你精通Redis?说说Redis哨兵
|
2月前
|
编解码 算法 定位技术
GEE时序——利用sentinel-2(哨兵-2)数据进行地表物候学分析(时间序列平滑法估算和非平滑算法代码)
GEE时序——利用sentinel-2(哨兵-2)数据进行地表物候学分析(时间序列平滑法估算和非平滑算法代码)
78 3
|
1月前
|
存储 监控 NoSQL
Redis 架构深入:主从复制、哨兵到集群
大家好,我是小康,今天我们来聊下 Redis 的几种架构模式,包括主从复制、哨兵和集群模式。
Redis 架构深入:主从复制、哨兵到集群
|
2月前
|
存储 编解码 人工智能
GEE数据集——哨兵2号Sentinel-2 云概率数据集
GEE数据集——哨兵2号Sentinel-2 云概率数据集
123 2
|
2月前
|
NoSQL Redis Docker
关于redis的一主三从三哨兵的实现
关于redis的一主三从三哨兵的实现
27 0
|
3月前
|
监控 NoSQL 程序员
Redis 高可用篇:你管这叫 Sentinel 哨兵集群原理
Redis 高可用篇:你管这叫 Sentinel 哨兵集群原理
77 5
|
3月前
|
NoSQL Linux Redis
Redis 6.X Sentinel 哨兵集群搭建
Redis 6.X Sentinel 哨兵集群搭建
32 5
|
6月前
|
存储 NoSQL 网络安全
Redis安装(单机、主从、哨兵、集群)
Redis安装(单机、主从、哨兵、集群)
132 1
|
4月前
|
缓存 NoSQL 应用服务中间件
分布式缓存之Redis(持久化、主从、哨兵、分片集群)
分布式缓存之Redis(持久化、主从、哨兵、分片集群)
|
9月前
|
存储 负载均衡 监控
Redis极简入门-Redis集群搭建-主从-哨兵-Cluster集群
Redis为什么做集群 采用Redis集群方案解决单点故障问题 对于高并发读写请求,采用集群方案提升Redis作业能力 处理大量数据,多个Redis节点实现数据的分布式存储
66 0

热门文章

最新文章