一、主从复制面临的问题

Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:

  • 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备“顶”上来,并且保证数据尽量不丢失(主从复制是最终一致性)​。
  • 从节点可以扩展主节点的读能力,一旦主节点不能支撑住大并发量的读操作,从节点可以在一定程度上帮助主节点分担读压力。

主从模式面临如下问题:

  • 一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,还需要命令其他从节点去复制新主节点,整个过程都需要人工干预。
  • 主节点的写能力收到单机的限制。
  • 主节点的存储能力收到单机的限制。

其中问题一的故障转移属于高可用性问题,而问题二和三则属于分布式问题可以采用集群模式解决。

二、高可用性

Redis 主从复制模式下,一旦主节点出现了故障不可达,需要人工干预进行故障转移,无论对于 Redis 的应用方还是运维方都带来了很大的不便。

  • 对于应用方来说无法及时感知到主节点的变化,必然会造成一定的写数据丢失和读数据错误,甚至可能造成应用方服务不可用。
  • 对于 Redis 的运维方来说,整个故障转移的过程是需要人工来介入的,故障转移实时性和准确性上都无法得到保障。

主从复制模式下主节点出现故障后,故障转移流程如下:

  • 主节点故障后,客户端连接主节点失败,两个从节点与主节点连接失败造成复制中断。
  • 选择一个从节点,对其执行 slaveof no one 命令,使其成为新的主节点。
  • 从节点成为新主节点后,更新应用方的主节点信息,并重启应用方。
  • 客户端命令另一个从节点去复制新的主节点。
  • 原来的主节点恢复后,让其去复制新的主节点。

上述处理过程可以认为整个服务或者架构的设计不是高可用的,因为整个故障转移的过程需要人介入。考虑到这点,有些公司把上述流程自动化了,但是仍然存在如下问题:

  • 第一,判断节点不可达的机制是否健全和标准。
  • 第二,如果有多个从节点,怎样保证只有一个被晋升为主节点。
  • 第三,通知客户端新的主节点机制是否足够健壮

三、使用 Redis Sentinel 达到高可用性

当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

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

Sentinel 在进行故障转移时,处理步骤如下:

  • 主节点出现故障,两个从节点与主节点失去连接。
  • 每个 Sentinel 节点通过定期监控发现主节点出现了故障。
  • 多个 Sentinel 节点对主节点的故障达成一致,从 Sentinel 节点集合中选举出一个节点作为领导者负责故障转移。
  • Sentinel 领导者节点执行了故障转移。故障转移过程与人工转移流程一致,不过是自动化完成的。

总结一下 Redis Sentinel 的功能:

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

Redis Sentinel 包含多个 Sentinel 节点,对于节点的故障判断是由多个 Sentinel 节点共同完成的,这样可以有效地防止误判。并且即使个别节点集合中的 Sentinel 节点不可用,整个 Sentinel 节点集合依然是健壮的。

Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令

四、Sentinel 节点的配置

Sentinel 可用配置一览,Sentinel 的默认配置可以从 Redis 源码文件夹下的 sentinel.conf 中查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
port 26379

sentinel monitor redis-master 127.0.0.1 6379 2

sentinel auth-pass redis-master yourpassword

sentinel down-after-milliseconds redis-master 3000

requirepass yourpassword

sentinel sentinel-pass yourpassword

sentinel parallel-syncs redis-master 1

sentinel failover-timeout redis-master 180000

SENTINEL resolve-hostnames yes

SENTINEL announce-hostnames yes

SENTINEL master-reboot-down-after-period redis-master 0

sentinel monitor redis-master 127.0.0.1 6379 2 中表示监控 127.0.0.1:6379 这个主节点,其中 2 表示判断主节点失败至少需要两个 Sentinel 节点同意。redis-master 是主节点的别名。

五、Redis Sentinel 实现原理

1、定时监控任务

Redis Sentinel 通过三个定时监控任务完成对各个节点的发现和监控:

  • 每隔 10 秒,每个 Sentinel 节点会向主节点和从节点发送 info 命令获取最新的拓扑结构。该定时任务的作用:

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

    • 发现新的 Sentinel 节点。通过订阅主节点的 __sentinel__:hello 频道了解其他 Sentinel 节点的信息,如果是新加入的 Sentinel 节点,将该 Sentinel 节点的信息保存下来,并与该 Sentinel 节点创建连接。
    • Sentinel 节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。
  • 每隔 1 秒,每个 Sentinel 节点会向主节点、从节点、其余 Sentinel 节点发送一条 ping 命令做一次心跳检测,来确认这些节点当前是否可达。通过该定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点的监控,这个定时任务是节点失败判定的重要依据。

2、主观下线和客观下线

上面提到的定时任务之一,每个 Sentinel 节点每隔 1 秒对主节点、从节点、其他 Sentinel 节点发送 ping 命令做心跳检测,当这些节点超过 down-after-milliseconds 没有进行有效回复,Sentinel 节点就会对该节点做失败判定。这个就叫做主观下线。主观下线为单个 Sentinel 节点自己的判断,未与其他 Sentinel 节点“沟通”。

当 Sentinel 主观下线的节点是主节点时,该 Sentinel 节点会通过 sentinel is-master-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断,当超过 <quorum> 个数,Sentinel 节点认为主节点确实有问题,这时该 Sentinel 节点会做出客观下线的决定。

从节点、Sentinel 节点在主观下线后,没有后续的故障转移操作。

3、领导者 Sentinel 节点选举

故障转移的工作只需要一个 Sentinel 节点来完成即可,所以 Sentinel 节点之间会做一个领导者选举的工作,选出一个 Sentinel 节点作为领导者进行故障转移的工作。

Redis 使用了 Raft 算法实现领导者选举,Redis Sentinel 进行领导者选举的大致流程如下:

  • 每个在线的 Sentinel 节点都有资格成为领导者,当它确认主节点客观下线的时候,会向其他 Sentinel 节点发送 sentinel is-master-down-by-addr 命令,要求将自己设置为领导者。
  • 收到命令的 Sentinel 节点,如果没有同意过其他 Sentinel 节点的 sentinel is-master-down-by-addr 命令,将同意该请求,否则拒绝。
  • 如果该 Sentinel 节点发现自己的票数已经大于等于 max(quorum,num(sentinels)/2+1),那么它将成为领导者。
  • 如果此过程没有选举出领导者,将进入下一次选举。

一旦有一个 Sentinel 节点获得了 max(quorum,num(sentinels)/2+1) 的票数,其他 Sentinel 节点再去确认已经没有意义了,因为每个 Sentinel 节点只有一票。

Sentinel 之间通过发送 RAFT 的投票协议消息来选举领导者。每个 Sentinel 节点都会给某一个请求它进行投票的 Sentinel 节点投票,但在整个选举过程中,每个 Sentinel 只能投一次票。通常情况下,第一个请求投票的 Sentinel 会获得同意。如果某个Sentinel 节点发现自己得到的票数已经超过半数且超过 <quorum>,那么它就成为领导者。如果这个过程中有多个 Sentinel 成为领导者,那么将等待一段时间重新进行选择,直到有且只有一个 Sentinel 节点成为领导者为止。

Raft 算法详情可查看 Raft Consensus Algorithm

4、故障转移

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

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

六、部署 Redis Sentinel 注意事项

  • 生产环境中建议 Redis Sentinel 的所有节点应该分布在不同的物理机上。

  • 部署至少三个且奇数个的 Sentinel 节点。

  • 如果 Sentinel 节点集合监控的是同一个业务的多个主节点集合,那么可以使用一套 Sentinel,否则一般建议使用多套 Sentinel。

    • 一套 Sentinel 指的是一个 Sentinel 节点集合监控多个主节点。
    • 多套 Sentinel 指的是有多个 Redis 主节点,每一个主节点都使用单独的 Sentinel 节点集合进行监控。
  • 副本优先级属性 replica-priority 的设置

    • 为硬件性能更强的从节点设置更高的优先级(replica-priority 数值越小),确保故障转移后新主节点能高效处理请求。
    • 弱某些节点因网络或资源限制不适合作为主节点,可将其 replica-priority 设置为 0 排除候选。

七、API 命令

1、sentinel is-master-down-by-addr

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

1
sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
  • ip,主节点的 IP
  • port,主节点端口
  • current_epoch,当前配置的纪元
  • runid,取值不同作用不同,分别作用于主管下线和领导者选举。
    • * 时,作用是 Sentinel 节点直接交换对主节点下线的判定。
    • 值等于当前 Sentinel 节点的 runid 时,作用是当前 Sentinel 节点希望目标 Sentinel 节点同意自己成为领导者的请求。

该命令的返回结果包含三个参数,如下所示:

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

2、sentinel monitor <master name> <ip> <port> <quorum>

此命令和配置文件中的含义完全一样,只不过是通过命令的形式来完成 Sentinel 节点对主节点的监控。

  • master name,自定义主节点的名称标识,用于区分不同的 Redis 服务。
  • ipport,指定主节点的 IP 地址和端口号。
  • quorum,触发主节点客观下线所需的最小 Sentinel 同意数量。该配置只用来判断客观下线。

相关链接

Raft Consensus Algorithm

Docker 中部署 Redis | z2huo

[[Docker 中部署 Redis#四、哨兵模式]]

OB tags

#Redis #未完待续