Redis-09-哨兵

基本概念

Redis Sentinel 相关名词解释

名词 逻辑结构 物理结构
主节点(master) Redis 主服务/数据库 一个独立的 Redis 进程
从节点(slave) Redis 从服务/数据库 一个独立的 Redis 进程
Redis 数据节点 主节点和从节点 主节点和从节点的进程
Sentinel 节点 监控 Redis 数据节点 一个独立的 Sentinel 进程
Sentinel 节点集合 若干 Sentinel 节点的抽象组合 若干 Sentinel 节点进程
Redis Sentinel Redis 高可用实现方案 Sentinel 节点集合和 Redis 数据节点进程
应用方 泛指一个或多个客户端 一个或者多个客户端进程或线程

主从复制的问题

  • 一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预
  • 主节点的写能力受到单机的限制
  • 主节点的存储能力受到单机的限制

Redis Sentinel 的高可用性

Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工
来介入,所以这套方案很有效地解决了 Redis 的高可用问题。

注意:这里的分布式是指 Redis 数据节点、Sentinel 节点集合、客户端分布在多个物理节点的架构。

Redis Sentinel 具有以下几个功能:

  • 监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达
  • 通知:Sentinel 节点会将故障转移的结果通知给应用方
  • 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系
  • 配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息

Sentinel 节点本身就是独立的 Redis 节点,它们不存储数据,只支持部分命令。

安装与部署

部署步骤

  1. 部署拓扑结构
  2. 部署 Redis 数据节点
    a) 启动主节点
    b) 启动从节点
    c) 确认主从关系

  3. 部署 Sentinel 节点
    a) 配置 Sentinel 节点
    b) 启动 Sentinel 节点
    c) 确认节点信息

配置优化

  1. 配置说明和优化

    • sentinel monitor <matser-name> <ip> <port> <quorum>
      • quorum 代表要判定主节点最终不可达所需要的票数
    • sentinel down-after-milliseconds <master-name> <times>
      • 每个 Sentinel 节点都要通过定期发送 ping 命令来判断 Redis 数据节点和其余Sentinel 节点是否可达,如果超过了 down-after-milliseconds 配置的时间且没有有效的回复,则判定节点不可达,times(单位为毫秒)就是超时时间。
      • down-after-milliseconds 虽然以 master-name 为参数,但实际上对 Sentinel节点、主节点、从节点的失败判定同时有效。
    • sentinel parallel-sysncs <master-name> <nums>
      • parallel-syncs 是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数
    • sentinel failover-timeout <master-name> <times>
      • failover-timeout 通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段:
        1) 选择合适从节点
        2) 将选出的从节点晋升为主节点
        3) 命令其他从节点复制新的主节点
        4) 等待原主节点恢复后命令它去复制新的主节点
      • failover-timeout 的作用具体体现在四个方面:
        • 如果 Redis Sentinel 对一个主节点故障转移失败,那么下次再对该主节点做故障转移的起始时间是 failover-timeout 的 2 倍
        • 在 2) 阶段时,如果 Sentinel 节点向 1) 阶段选出来的从节点执行 slaveof no one 一直失败(例如该从节点此时出现故障),当此过程超 failover-timeout 时,则故障转移失败
        • 在 2) 阶段如果执行成功,Sentinel 节点还会执行 info 命令来确认 1) 阶段选出来的节点确实晋升为主节点,如果此过程执行时间超过 failover-timeout时,则故障转移失败
        • 如果 3) 阶段执行时间超过了 failover-timeout(不包含复制时间),则故障转移失败。注意即使超过了这个时间,Sentinel 节点也会最终配置从节点去同步最新的主节点
    • sentinel auth-pass <mymaster-name> <password>
      • 如果 Sentinel 监控的主节点配置了密码,sentinel auth-pass 配置通过添加主节点的密码,防止 Sentinel 节点对主节点无法监控
    • sentinel notification-script <mymaster-name> <script-path>
      • sentinel notification-script 的作用是在故障转移期间,当一些警告级别的Sentinel 事件发生(指重要事件,例如-sdown:客观下线、-odown:主观下线)时,会触发对应路径的脚本,并向脚本发送相应的事件参数
    • sentinel client-reconfig-script <master-name> <script-path>
      • sentinel client-reconfig-script 的作用是在故障转移结束后,会触发对应路径的脚本,并向脚本发送故障转移结果的相关参数
  2. 监控多个节点

    Redis Sentinel 可以同时监控多个主节点,只需要指定多个 masterName 来区分不同的主节点即可。

  3. 调整配置

    sentinel set <param> <value>

    注意:

    • sentinel set 命令只对当前 Sentinel 节点有效
    • sentinel set 命令如果执行成功会立即刷新配置文件,这点和 Redis 普通数据节点设置配置需要执行 config rewrite 刷新到配置文件不同
    • 建议所有 Sentinel 节点的配置尽可能一致,这样在故障发现和转移时比较容易达成一致
    • Sentinel 对外不支持 config 命令

部署技巧

  1. Sentinel 节点不应该部署在一台物理机器上
  2. 部署至少三个且奇数个的 Sentinel 节点
  3. 只有一套 Sentinel,还是每个主节点配置一套 Sentinel?
    • 如果 Sentinel 节点集合监控的是同一个业务的多个主节点集合,那么使用一套 Sentinel,否则一般建议采用多套 Sentinel

API

  1. sentinel masters:展示被监控的主节点状态以及相关的统计信息
  2. sentinel master <master name>:展示指定 master name 的主节点状态以及相关的统计信息
  3. sentinel slaves <master name>:展示指定 master name 的从节点状态以及相关的统计信息
  4. sentinel sentinels <master name>:展示指定 master name 的 sentinel 节点集合(不包含当前 sentinel 节点)
  5. sentinel get-master-addr-by-name <master name>:返回指定 master name 主节点的 IP 地址和端口
  6. sentinel reset <pattern>:当前 Sentinel 节点对符合 pattern(通配符风格)主节点的配置进行重置,包含清除主节点的相关状态(例如故障转移),重新发现从节点和 Sentinel 节点
  7. sentinel failover <master name>:对指定 master name 主节点进行强制故障转移(没有和其他 Sentinel 节点“协商”),当故障转移完成后,其他 Sentinel 节点按照故障转移的结果更新自身配置
  8. sentinel ckquorum <master name>:检测当前可达的 Sentinel 节点总数是否达到 quorum 的个数
  9. sentinel flushconfig:将 Sentinel 节点的配置强制刷到磁盘上
  10. sentinel remove <master name>:取消当前 Sentinel 节点对于指定 master name 主节点的监控,注意这个命令仅仅对当前 Sentinel 节点有效
  11. sentinel monitor <matser name> <ip> <port> <quorum>
  12. sentinel set <master name>:动态修改 Sentinel 节点配置选项
  13. sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>:Sentinel 节点之间用来交换对主节点是否下线的判断,根据参数的不同,还可以作为 Sentinel 领导者选举的通信方式

Sentinel 节点只支持如下命令:ping、sentinel、subscribe、unsubscribe、psubscribe、punsubscribe、publish、info、role、client、shutdown。

Redis Sentinel 客户端

实现一个 Redis Sentinel 客户端的基本步骤如下:

  1. 遍历 Sentinel 节点集合获取一个可用的 Sentinel 节点
  2. 通过 sentinel get-master-addr-by-name master-name 这个 API 来获取对应主节点的相关信息
  3. 验证当前获取的“主节点”是真正的主节点,这样做的目的是为了防止故障转移期间主节点的变化
  4. 保持和 Sentinel 节点集合的“联系”,时刻获取关于主节点的相关“信息”

实现原理

三个定时监控任务

  1. 每隔 10 秒,每个 Sentinel 节点会向主节点和从节点发送 info 命令获取最新的拓扑结构

    • 通过向主节点执行 info 命令,获取从节点的信息,这也是为什么 Sentinel 节点不需要显式配置监控从节点
    • 当有新的从节点加入时都可以立刻感知出来
    • 节点不可达或者故障转移后,可以通过 info 命令实时更新节点拓扑信息
  2. 每隔 2 秒,每个 Sentinel 节点会向 Redis 数据节点的 __sentinel__:hello 频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息,同时每个 Sentinel 节点也会订阅该频道,来了解其他 Sentinel 节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:

    • 发现新的 Sentinel 节点
    • Sentinel 节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据
    • Sentinel 节点 publish 的消息格式:<Sentinel IP> <Sentinel port> <Sentinel runId> <Sentinel 节点配置版本> <主节点名字> <主节点 IP> <主节点端口> <主节点配置版本>
  3. 每隔 1 秒,每个 Sentinel 节点会向主节点、从节点、其余 Sentinel 节点发送一条 ping 命令做一次心跳检测,来确认这些节点当前是否可达

主观下线和客观下线

  1. 主观下线:心跳检测中超过 down-after-milliseconds 没有进行有效回复,Sentinel 节点就会对该节点做失败判定,这个行为叫做主观下线
  2. 当 Sentinel 主观下线的节点是主节点时,该 Sentinel 节点会通过 sentinel is-master-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断,当超过 quorum 个数,Sentinel 节点认为主节点确实有问题,这时该 Sentinel 节点会做出客观下线的决定

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

  • ip:主节点 IP
  • port:主节点端口
  • current_epoch:当前配置纪元
  • runid:此参数有两种类型,不同类型决定了此 API 作用的不同
    • 当 runid 等于 * 时,作用是 Sentinel 节点直接交换对主节点下线的判定
    • 当 runid 等于当前 Sentinel 节点的 runid 时,作用是当前 Sentinel 节点希望目标 Sentinel 节点同意自己成为领导者的请求

询问对主节点的判断命令的返回结果包括:

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

领导者 Sentinel 节点选举

Redis 使用了 Raft 算法 实现领导者选举

故障转移

领导者选举出的 Sentinel 节点负责故障转移,具体步骤如下:

  1. 在从节点列表中选出一个节点作为新的主节点,选择方法如下:

    1. 过滤:“不健康”(主观下线、断线)、5 秒内没有回复过 Sentinel 节点 ping 响应、与主节点失联超过 $down-after-milliseconds * 10$ 秒
    2. 选择 slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续
    3. 选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续
    4. 选择 runid 最小的从节点
  2. Sentinel 领导者节点会对第一步选出来的从节点执行 slaveof no one 命令让其成为主节点

  3. Sentinel 领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和 parallel-syncs 参数有关

  4. Sentinel 节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点