" name="sm-site-verification"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 206 篇文章
  • 累计创建 14 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Redis 高可用 -- 哨兵模式(故障自动转移)

PySuper
2021-12-07 / 0 评论 / 0 点赞 / 7 阅读 / 0 字
温馨提示:
本文最后更新于2024-05-28,若内容或图片失效,请留言反馈。 所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

基本概念

主从复制的机制是其他高可用方式的基础,下面介绍的哨兵方式和集群方式都依赖于主从复制机制


  • 若干 Sentinel 节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知
  • 节点数量要满足 2n+1(n>=1)的奇数个

  • 哨兵节点: 哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据
  • 数据节点: 主节点和从节点都是数据节点

哨兵方式在主从复制的基础上,实现了故障自动切换的功能:

  • 监控: 哨兵会不断地检查主节点和从节点是否运作正常(发布-订阅
  • 自动故障转移
    • 当主节点不能正常工作时,哨兵会开始 自动故障转移操作
    • 它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点
  • 配置提供者: 客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址
  • 通知:哨兵可以将故障转移的结果发送给客户端

配置及使用

一主二从三哨兵的架构来搭建一个哨兵系统

数据节点

找到 redis.conf 文件复制三份: redis-master.conf、redis-slave1.conf、redis-slave2.conf

# redis-master.conf    master
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
 
# redis-slave1.conf   slave-1
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 127.0.0.1 6380
 
# redis-slave2.conf    slave-2
port 6382
daemonize yes
logfile "6382.log"
dbfilename "dump-6382.rdb"
slaveof 127.0.0.1 6380


####################################################################################

# 启动三个 redis 实例
/usr/local/redis/bin/redis-server redis-master.conf
/usr/local/redis/bin/redis-server redis-slave1.conf
/usr/local/redis/bin/redis-server redis-slave2.conf

####################################################################################

# 查看redis进程
ps -aux | grep redis

####################################################################################

# 连接主节点,查看状态(info Replication)
redis -h 127.0.0.1 -p 6380

redis-sentinel-1.png

哨兵节点

哨兵节点本质上是特殊的 Redis 节点,所以配置几乎没什么差别,只是在端口上做区分

# redis-sentinel-1.conf
port 16380
daemonize yes
logfile "16380.log"
sentinel monitor redis-master 127.0.0.1 6380 2

# redis-sentinel-2.conf
port 16381
daemonize yes
logfile "16381.log"
sentinel monitor redis-master 127.0.0.1 6380 2

# redis-sentinel-3.conf
port 16382
daemonize yes
logfile "16382.log"
sentinel monitor redismaster 127.0.0.1 6380 2

####################################################################################

# 启动哨兵节点
/usr/local/redis/bin/redis-server redis-sentinel-1.conf --sentinel
/usr/local/redis/bin/redis-server redis-sentinel-2.conf --sentinel
/usr/local/redis/bin/redis-server redis-sentinel-3.conf --sentinel


####################################################################################

# 登录哨兵节点,查看状态(info Sentinel)
redis -h 127.0.0.1 -p 16380

sentinel monitor redis-master 127.0.0.1 6380 2

  • 该哨兵节点监控 127.0.0.1:63780 这个主节点,该主节点的名称是 redis-master
  • `2` :主节点的故障判定有关,至少需要 `2` 个哨兵节点同意,才能判定主节点故障并进行故障转移
    
redis-sentinel-2.png
redis-sentinel-3.png

Python连接

from redis import Sentinel

sentinel = Sentinel(
    [('127.0.0.1', 16380), ('127.0.0.1', 16381), ('127.0.0.1', 16382)],
    socket_timeout=0.5
)

# 获取主服务器地址,discover_master 参数为自己的mastername
master = sentinel.discover_master('redis-master')
print(master)

Sentinel命令

# 展示所有被监控的主节点状态以及相关的统计信息
sentinel masters

# 展示指定<master name>的主节点状态以及相关的统计信息
sentinel master <master name>

# 展示指定<master name>的从节点状态以及相关的统计信息
sentinel slaves <master name>

# 展示指定<master name>的Sentinel节点集合(不包含当前Sentinel节点)
sentinel sentinels <master name>

# 返回指定<master name>主节点的IP地址和端口
sentinel get-master-addr-by-name <master name>

# 对指定<master name>主节点进行强制故障转移
sentinel failover <master name>

# 检测当前可达的Sentinel节点总数是否达到<quorum>的个数
sentinel ckquorum <master name>

# 取消当前Sentinel节点对于指定<master name>主节点的监控
sentinel remove <master name>

# 通过命令的形式来完成Sentinel节点对主节点的监控
sentinel monitor <master name> <ip> <port> <quorum>

# 动态修改Sentinel节点配置选项
sentinel set <master name>

# Sentinel节点之间用来交换对主节点是否下线的判断
# 根据参数的不同,还可以作为Sentinel领导者选举的通信方式
sentinel is-master-down-by-addr

流程及原理

定时监控

  • 每隔 10 秒,每个 Sentinel 节点会向主节点和从节点发送 info 命令获取 Redis 数据节点的信息
    • 这也是为什么 Sentinel 节点不需要显式配置监控从节点
    • 当有新的从节点加入时都可以立刻感知出来
    • 节点不可达或者故障转移后,可以通过 info 命令实时更新节点拓扑信息
  • 每隔 2 秒,每个 Sentinel 节点会向 Redis 数据节点的__sentinel__:hello 频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息,同时每个 Sentinel 节点也会订阅该频道,来了解其他Sentinel 节点以及它们对主节点的判断
    • 消息格式
      • s_ip:sentinel的ip
      • s_port:sentinel的端口
      • s_runid:sentinel云心id
      • s_epoch:sentinel当前的配置纪元
      • m_name:主服务器名字
      • m_ip:主服务器ip
      • m_port:主服务器端口
      • m_epoch:主服务器纪元
    • 发现新的 Sentinel 节点
      • 通过订阅主节点的__sentinel__:hello了解其他的 Sentinel 节点信息
      • 如果是新加入的 Sentinel 节点,将该 Sentinel 节点信息保存起来(sentinelRedisInstance)
      • 并与该 Sentinel 节点创建连接
redis-sentinel-4.png
- Sentinel 节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据 - 每隔 1 秒,每个 Sentinel 节点会向主节点、从节点、其余 Sentinel 节点发送一条 ping 命令做一次心跳检测,来确认这些节点当前是否可达
redis-sentinel-5.png

主观/客观下线

主观下线

单个 sentinel 认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)

sentinel 会以每秒一次的频率向所有与其建立了命令连接的实例(master、slave、其他 sentinel)发 ping 命令
通过判断 ping 回复是有效回复还是无效回复,来判断实例是否在线(对该 sentinel 来说是主观在线

  • sentinel 配置文件中的 down-after-milliseconds 设置了判断主观下线的时间长度
  • 如果实例在 down-after-milliseconds 毫秒内返回的都是无效回复,那么 sentinel 会认为该实例已下线(主观)
  • 并修改其 flags 状态为 SRI_S_DOWN

在实际的工作当中,多个 sentinel 配置的 down-after-milliseconds 超时时间推荐一致

客观下线

只针对主节点,从节点和哨兵节点不需要这一步

当 Sentinel 主观下线的节点是主节点时:

  • 该 Sentinel 节点会通过 sentinel ismaster-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断
  • 当超过配置中的<quorum>个数,Sentinel 节点认为主节点确实有问题
  • 这时该 Sentinel 节点会做出客观下线的决定,并后续对其做故障转移操作

如果每个 Sentinel 配置的 down-after-milliseconds 时间不一致,会很难超过<quorum>配置的个数

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

eg:
	sentinel is-master-down-by-addr 127.0.0.1 6379 0 *
  • ip:主节点 IP
  • port:主节点端口
  • current_epoch:当前配置纪元
  • runid:此参数有两种类型,当 runid为*
    • Sentinel 节点直接交换对主节点下线的判定
    • 在领导者Sentinel 节点选举时,runid 等于当前 Sentinel 节点的 runid
    • 作用是当前 Sentinel 节点,希望目标 Sentinel 节点同意自己成为领导者的请求

一个 sentinel 接收另一个 sentinel 发来的 is-master-down-by-addr 后

提取参数,根据 ip 和端口,检测该服务时候在该 sentinel 主观下线,并且回复 is-master-down-by-addr

  • down_state:目标 Sentinel 节点对于主节点的下线判断,1 是下线,0 是在线
  • leader_runid:
    • *:代表返回结果是用来做主节点是否不可达
    • 具体的 runid:代表目标节点同意 runid 成为领导者
  • leader_epoch:领导者纪元

领导者 Sentinel 选举

Redis 使用了 Raft 算法实现领导者选举,大体思路:

  • 每个在线的 Sentinel 节点都有资格成为领导者,每个 Sentinel 节点发现当它确认主节点客观下线检查时候,会向其他 Sentinel 节点发送 sentinel is-master-down-by-addr 命令,要求将自己设置为领导者
  • 每个节点在每个选举轮次中只有一次投票权,收到命令的 Sentinel 节点,如果没有同意过其他 Sentinel 节点的 sentinelis-master-down-by-addr 命令,将同意该请求,否则拒绝
  • 如果该 Sentinel 节点发现自己的票数已经大于等于 max(quorum,num(sentinels)/2+1),那么它将成为领导者。其他的投票就会终止,即使后续还有其他的哨兵节点到达配置,也没有作用
  • 如果此过程没有选举出领导者, 暂定一段时间,再进行下一轮选举,current_epoch 加 1

选举流程

  • s1 (哨兵节点) 节点首先完成客观下线的检查,然后向 s2 和 s3 发送成为领导者的请求
  • s2 节点完成客观下线的检查,然后向 s1 和 s3 发送成为领导者的请求
  • s3节点完成客观下线的检查,然后向 s1和 s2发送成为领导者的请求
redis-sentinel-6.png

故障转移

  • 在从节点列表中选出一个作为新的主节点
    • 过滤
      • “不健康”节点(主观下线、断线)
      • 5 秒内没有回复过 Sentinel 节点 ping 响应
      • 与主节点失联超过 down-after-milliseconds*10s(断线时间越长主从数据不一致问题越严重)
    • 选择 slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续
    • 如果优先级一样,选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续
    • 如果偏移量一样,选择 runid 最小的从节点
    • 挑选从节点的重要原则:在从节点列表中挑选与主节点数据最一致的节点
  • Sentinel 领导者节点会对第一步选出来的从节点执行 slaveof no one 命令让其成为主节点
  • Sentinel 领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和 parallel-syncs 参数有关:slaveof ip port
  • Sentinel 节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点

优点

  • 在Redis主从复制的基础上,增加了故障自动转移
  • 方便实现 Redis 数据节点的线形扩展
  • 突破 Redis 自身单线程瓶颈,可较大程度上满足Redis 大容量或高性能的业务需求
  • 可以实现一套 Sentinel 监控一组 Redis 数据节点或多组数据节点

缺点

  • 部署相对 Redis 主从模式要复杂一些,原理理解更繁琐
  • 资源浪费,Redis 数据节点中slave 节点作为备份节点不提供服务
  • Redis Sentinel 主要是针对主节点的高可用切换:
    • 主观下线(并不执行故障转移)
    • 客观下线
  • 不能解决读写分离问题,实现起来相对复杂

全部配置

# Example sentinel.conf

# 哨兵sentinel实例运行的端口 默认26379
port 26379

# 哨兵sentinel的工作目录
dir /tmp

# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000

# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1

# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000

# SCRIPTS EXECUTION
# 配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
# 对于脚本的运行结果有以下规则:
# 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
# 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
# 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# 一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 通知型脚本: 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
# 通知脚本
# shell编程
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh

# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh


0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区