05_哨兵
Redis Sentinel(哨兵)
Redis的主从复制模式下,一旦主节点由于故障不能提供服务,就会发生群龙无首的情况,如果在主机宕机时,能够在从机中选出一个来充当主机,那么就不用我们每次去手动重启主机了,Redis从2.8开始正式提供了Redis Sentinel(哨兵) 架构来解决这个问题 。
1、基本概念
名词 | 逻辑结构 | 物理结构 |
---|---|---|
主节点(master) | Redis主服务/数据库 | 一个独立的Redis进程 |
从节点(slave) | Redis从服务/数据库 | 一个独立的Redis进程 |
名词 | 逻辑结构 | 物理结构 |
---|---|---|
Redis数据节点 | 主节点和从节点 | 主节点和从节点的进程 |
Sentinel节点 | 监控Redis数据节点 | 一个独立的Sentinel进程 |
Sentinel节点集合 | 若干Sentinel节点的抽象组合 | 若干Sentinel节点进程 |
Redis Sentinel | Redis高可用实现方案 | Sentinel节点集合和Redis数据节点进程 |
应用方 | 泛指一个或多个客户端 | 一个或者多个客户端进程或线程 |
1.1 主从复制的问题
Redis的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:第一,作为主节点的一个备份,一旦主节点除了故障不可达的情况,从节点可以作为后备顶上来,并且保证数据尽量不丢失(主从复制是最终一致性)。第二,从节点可以扩展主节点的读能力,一旦主节点不能支撑住大并发量的读操作,从节点可以在一定程度上帮助主节点分担读压力。
但是主从复制也带来了以下问题:
- 一旦主节点出现故障, 需要手动将一个从节点晋升为主节点, 同时需要修改应用方的主节点地址, 还需要命令其他从节点去复制新的主节点, 整个过程都需要人工干预。
- 主节点的写能力受到单机的限制。
- 主节点的存储能力受到单机的限制 。
其中第一个问题就是Redis的高可用问题 ,将在以下进行介绍。第二、 三个问题属于Redis的分布式问题 。
1.2 高可用
Redis主从复制模式下, 一旦主节点出现了故障不可达,对于应用方来说无法及时感知到主节点的变化, 必然会造成一定的写数据丢失和读数据错误, 甚至可能造成应用方服务不可用。图1-1到图1-2展示了一个1主2从的Redis主从复制模式下的主节点出现故障后, 是如何进行故障转移的, 过程如下所示。
1)主节点发生故障后,客户端(client)连接主节点失败,两个从节点与主节点连接失败造成复制中断。

2) 如果主节点无法正常启动, 需要选出一个从节点(slave-1) , 对其执行slaveof no one命令使其成为新的主节点。

3)原来的从节点(slave-1) 成为新的主节点后, 更新应用方的主节点信息, 重新启动应用方。

4)客户端命令另一个从节点(slave-2) 去复制新的主节点(new-master)

5)待原来的主节点恢复后, 让它去复制新的主节点。

上述处理过程就可以认为整个服务或者架构的设计不是高可用的, 因为整个故障转移的过程需要人介入。即使把上述流程自动化了, 但是仍然存在如下问题:
- 第一, 判断节点不可达的机制是否健全和标准。
- 第二, 如果有多个从节点, 怎样保证只有一个被晋升为主节点。
- 第三,通知客户端新的主节点机制是否足够健壮。
Redis Sentinel正是用于解决这些问题 !
1.3 Redis Sentinel的高可用性
当主节点出现故障时, Redis Sentinel能自动完成故障发现和故障转移,并通知应用方, 从而实现真正的高可用。
注意
Redis Sentinel(Redis高可用实现方案)是一个分布式架构,其中包含若干个
Sentinel
节点和Redis数据节点
,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作, 同时会将这个变化实时通知给Redis应用方。 整个过程完全是自动的, 不需要人工来介入, 所以这套方案很有效地解决了Redis的高可用问题。这里的分布式是指:Redis数据节点、Sentinel节点集合、客户端分布在多个物理节点的架构,不要与Redis Cluster分布式混淆。
Redis Sentinel与Redis主从复制模式只是多了若干Sentinel节点, 所以Redis Sentinel并没有针对Redis节点做了特殊处理,
Redis主从复制与Redis Sentinel架构的区别
从逻辑架构上看, Sentinel节点集合会定期对所有节点进行监控, 特别是对主节点的故障实现自动转移。
下面以1个主节点、 2个从节点、 3个Sentinel节点组成的Redis Sentinel为例子进行说明, 拓扑结构如图所示。

整个故障转移的处理逻辑有下面4个步骤:
如图所示, 主节点出现故障, 此时两个从节点与主节点失去连接, 主从复制失败。
每个Sentinel节点通过定期监控发现主节点出现了故障。
多个Sentinel节点对主节点的故障达成一致, 选举出sentinel-3节点作为领导者负责故障转移。
Sentinel领导者节点执行了故障转移, 整个过程和高可用一样的,只不过是自动化的。
故障转移后整个Redis Sentinel的拓扑结构图。
通过上面介绍的Redis Sentinel逻辑架构以及故障转移的处理, 可以看出Redis Sentinel具有以下几个功能:
- 监控: Sentinel节点会定期检测Redis数据节点、 其余Sentinel节点是否可达。
- 通知: Sentinel节点会将故障转移的结果通知给应用方。
- 主节点故障转移: 实现从节点晋升为主节点并维护后续正确的主从关系。
- 配置提供者: 在Redis Sentinel结构中, 客户端在初始化的时候连接的是Sentinel节点集合, 从中获取主节点信息。
同时看到, Redis Sentinel包含了若个Sentinel节点, 这样做也带来了两个好处:
- 对于节点的故障判断是由多个Sentinel节点共同完成, 这样可以有效地防止误判。
- Sentinel节点集合是由若干个Sentinel节点组成的, 这样即使个别Sentinel节点不可用, 整个Sentinel节点集合依然是健壮的。
但是Sentinel节点本身就是独立的Redis节点, 只不过它们有一些特殊,它们不存储数据, 只支持部分命令。
2、安装和部署
2.1 部署拓扑结构
以3个Sentinel节点、 1个主节点、 2个从节点组成一个RedisSentinel进行说明, 拓扑结构如图所示。

具体的物理部署如表所示。

2.2 部署Redis数据节点
Redis Sentinel中Redis数据节点没有做任何特殊配置, 以一个比较简单的配置进行说明。
启动主节点
配置:
redis-6379.conf port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb" dir "/opt/soft/redis/data/"
启动主节点:
redis-server redis-6379.conf
确认是否启动。 一般来说只需要ping命令检测一下就可以, 确认Redis数据节点是否已经启动。
$ redis-cli -h 127.0.0.1 -p 6379 ping PONG
启动两个从节点
配置: 两个从节点的配置是完全一样的, 下面以一个从节点为例子进行说明,和主节点的配置不一样的是添加了slaveof配置。
redis-6380.conf port 6380 daemonize yes logfile "6380.log" dbfilename "dump-6380.rdb" dir "/opt/soft/redis/data/" slaveof 127.0.0.1 6379
启动两个从节点:
redis-server redis-6380.conf redis-server redis-6381.conf
验证:
$ redis-cli -h 127.0.0.1 -p 6380 ping PONG $ redis-cli -h 127.0.0.1 -p 6381 ping PONG
确认主从关系
主节点的视角, 它有两个从节点, 分别是127.0.0.1: 6380和127.0.0.1:6381
$ redis-cli -h 127.0.0.1 -p 6379 info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6380,state=online,offset=281,lag=1 slave1:ip=127.0.0.1,port=6381,state=online,offset=281,lag=0 .................
从节点的视角, 它的主节点是127.0.0.1: 6379:
$ redis-cli -h 127.0.0.1 -p 6380 info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up .................
此时拓扑结构如图所示。
添加两个从节点。
2.3 部署Sentinel节点
3个Sentinel节点的部署方法是完全一致的(端口不同) , 下面以sentinel-1节点的部署为例子进行说明。
配置Sentinel节点
redis-sentinel-26379.conf port 26379 daemonize yes logfile "26379.log" dir /opt/soft/redis/data sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
- Sentinel节点的默认端口是26379。
- sentinel monitor mymaster127.0.0.1 6379 2配置代表sentinel-1节点需要监控127.0.0.1: 6379这个主节点, 2代表判断主节点失 败至少需要2个Sentinel节点同意, mymaster是主节点的别名。
启动Sentinel节点
Sentinel节点的启动方法有两种:
方法一, 使用redis-sentinel命令:
redis-sentinel redis-sentinel-26379.conf
- 方法二, 使用redis-server命令加--sentinel参数:
redis-server redis-sentinel-26379.conf --sentinel
两种方法本质上是一样的。
确认
从下面info命令查询的Sentinel片段来看,Sentinel节点找到了主节点127.0.0.1: 6379, 发现了它的两个从节点, 同时发现Redis Sentinel一共有3个Sentinel节点。 这里只需要了解Sentinel节点能够彼此感知到对方, 同时能够感知到Redis数据节点就可以了:
$ redis-cli -h 127.0.0.1 -p 26379 info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
当三个Sentinel节点都启动后, 整个拓扑结构如图所示。
2.4 配置优化
配置说明和优化
port 26379
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
#sentinel auth-pass <master-name> <password>
#sentinel notification-script <master-name> <script-path>
#sentinel client-reconfig-script <master-name> <script-path>
(1) sentinel monitor
配置如下:
sentinel monitor <master-name> <ip> <port> <quorum>
本配置说明Sentinel节点要监控的是一个名字叫做“master-name”, ip地址和端口为标签“ip”标签“port”的主节点。 标签quorum代表要判定主节点最终不可达所需要的票数。但实际上Sentinel节点会对所有节点进行监控, 但是在Sentinel节点的配置中没有看到有关从节点和其余Sentinel节点的配置, 那是因为Sentinel节点会从主节点中获取有关从节点以及其余Sentinel节点的相关信息。
例如某个Sentinel初始节点配置如下:
port 26379
daemonize yes
logfile "26379.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
当所有节点启动后, 配置文件中的内容发生了变化, 体现在三个方面:
- Sentinel节点自动发现了从节点、 其余Sentinel节点。
- 去掉了默认配置, 例如parallel-syncs、 failover-timeout参数。
- 添加了配置纪元相关参数。
启动后变化为:
port 26379
daemonize yes
logfile "26379.log"
dir "/opt/soft/redis/data"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
#发现两个slave节点
sentinel known-slave mymaster 127.0.0.1 6380
sentinel known-slave mymaster 127.0.0.1 6381
#发现两个sentinel节点
sentinel known-sentinel mymaster 127.0.0.1 26380 282a70ff56c36ed56e8f7ee6ada741
24140d6f53
sentinel known-sentinel mymaster 127.0.0.1 26381 f714470d30a61a8e39ae031192f1fe
ae7eb5b2be
sentinel current-epoch 0
标签quorum还与Sentinel节点的领导者选举有关, 至少要有 max(quorum, num(sentinels) /2+1)
个Sentinel节点参与选举, 才能选出领导者Sentinel, 从而完成故障转移。 例如有5个Sentinel节点, quorum=4, 那么至少要有max(quorum, num(sentinels) /2+1) =4个在线Sentinel节点才可以进行领导者选举。
(2) sentinel down-after-milliseconds
配置如下:
sentinel down-after-milliseconds 标签master-name 标签times
每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达, 如果超过了down-after-milliseconds配置的时间且没有有效的回复, 则判定节点不可达, 标签times(单位为毫秒) 就是超时时间。 这个配置是对节点失败判定的重要依据。
优化说明: down-after-milliseconds越大, 代表Sentinel节点对于节点不可达的条件越宽松, 反之越严格。 条件宽松有可能带来的问题是节点确实不可达了, 那么应用方需要等待故障转移的时间越长, 也就意味着应用方故障时间可能越长。 条件严格虽然可以及时发现故障完成故障转移, 但是也存在一定的误判率。
(3) sentinel parallel-syncs
配置如下:
sentinel parallel-syncs <master-name> <nums>
parallel-syncs
就是用来限制在一次故障转移之后, 每次向新的主节点发起复制操作的从节点个数。 如果这个参数配置的比较大, 那么多个从节点会向新的主节点同时发起复制操作, 尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制, 必然会对主节点所在的机器造成一定的网络和磁盘IO开销。
parallelsyncs=3会同时发起复制, parallel-syncs=1时从节点会轮询发起复制。
(4) sentinel failover-timeout
配置如下(部署文档时总是提示标签缺失,所以用中文代替):
sentinel failover-timeout 标签master-name 标签times
failover-timeout通常被解释成故障转移超时时间, 但实际上它作用于故障转移的各个阶段:
a) 选出合适从节点。 b) 晋升选出的从节点为主节点。 c) 命令其余从节点复制新的主节点。 d) 等待原主节点恢复后命令它去复制新的主节点。
failover-timeout的作用具体体现在四个方面:
1) 如果Redis Sentinel对一个主节点故障转移失败, 那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
2) 在b) 阶段时, 如果Sentinel节点向a) 阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障) , 当此过程超过failover-timeout时, 则故障转移失败。
3) 在b) 阶段如果执行成功, Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点, 如果此过程执行时间超过 failovertimeout时, 则故障转移失败。
4) 如果c) 阶段执行时间超过了failover-timeout(不包含复制时间) ,则故障转移失败。 注意即使超过了这个时间, Sentinel节点也会 最终配置从节点去同步最新的主节点。
(5) sentinel auth-pass
配置如下:
sentinel auth-pass <master-name> <password>
如果Sentinel监控的主节点配置了密码, sentinel auth-pass配置通过添加主节点的密码, 防止Sentinel节点对主节点无法监控。
(6) sentinel notification-script
配置如下:
sentinel notification-script <master-name> <script-path>
sentinel notification-script的作用是在故障转移期间, 当一些警告级别的Sentinel事件发生(指重要事件, 例如-sdown: 客观下线、 -odown: 主观下线) 时, 会触发对应路径的脚本, 并向脚本发送相应的事件参数。
(7) sentinel client-reconfig-script
配置如下:
sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script的作用是在故障转移结束后, 会触发对应路径的脚本, 并向脚本发送故障转移结果的相关参数。
如何监控多个主节点
Redis Sentinel可以同时监控多个主节点。配置方法也比较简单, 只需要指定多个masterName来区分不同的主节点即可, 例如下面的配置监控monitor master-business-1(10.10.xx.1: 6379) 和monitor master-business-2(10.10.xx.2: 6379) 两个主节点:
sentinel monitor master-business-1 10.10.xx.1 6379 2
sentinel down-after-milliseconds master-business-1 60000
sentinel failover-timeout master-business-1 180000
sentinel parallel-syncs master-business-1 1
sentinel monitor master-business-2 10.16.xx.2 6380 2
sentinel down-after-milliseconds master-business-2 10000
sentinel failover-timeout master-business-2 180000
sentinel parallel-syncs master-business-2 1

调整配置
和普通的Redis数据节点一样, Sentinel节点也支持动态地设置参数, 而且和普通的Redis数据节点一样并不是支持所有的参数, 具体使用方法如下:
sentinel set <param> <value>
sentinel set命令支持的参数

有几点需要注意一下:
1) sentinel set命令只对当前Sentinel节点有效。 2) sentinel set命令如果执行成功会立即刷新配置文件,这点和Redis普通数据节点设置配置需要执行config rewrite刷新到配置文件不同。 3) 建议所有Sentinel节点的配置尽可能一致, 这样在故障发现和转移时比较容易达成一致。
4) Sentinel对外不支持config命令。
3、客户端连接
3.1 Java操作Redis Sentinel
Jedis针对Redis Sentinel给出了一个JedisSentinelPool, 很显然这个连接池保存的连接还是针对主节点的。 Jedis给出很多构造方法, 其中最全的如下所示:
public JedisSentinelPool(String masterName, Set<String> sentinels,
final GenericObjectPoolConfig poolConfig, final int connectionTimeout,
final int soTimeout,
final String password, final int database,
final String clientName)
具体参数含义如下:
- masterName——主节点名。
- sentinels——Sentinel节点集合。
- poolConfig——common-pool连接池配置。
- connectTimeout——连接超时。
- soTimeout——读写超时。
- password——主节点密码。
- database——当前数据库索引。
- clientName——客户端名。
例如要想通过简单的几个参数获取JedisSentinelPool, 可以直接按照下面方式进行JedisSentinelPool的初始化。
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName,
sentinelSet, poolConfig, timeout);
此时timeout既代表连接超时又代表读写超时, password为空, database默认使用0, clientName为空。
我们在使用JedisSentinelPool时也要尽可能按照common-pool的标准模式进行代码的书写,
Jedis jedis = null;
try {
jedis = jedisSentinelPool.getResource();
// jedis command
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null)
//并不是关闭Jedis连接。
jedis.close();
}
4、本章重点回顾
- Redis Sentinel是Redis的高可用实现方案: 故障发现、 故障自动转移、 配置中心、 客户端通知。
- Redis Sentinel从Redis2.8版本开始才正式生产可用, 之前版本生产不可用。
- 尽可能在不同物理机上部署Redis Sentinel所有节点。
- Redis Sentinel中的Sentinel节点个数应该为大于等于3且最好为奇数。
- Redis Sentinel中的数据节点与普通数据节点没有区别。
- 客户端初始化时连接的是Sentinel节点集合, 不再是具体的Redis节点, 但Sentinel只是配置中心不是代理。
- Redis Sentinel通过三个定时任务实现了Sentinel节点对于主节点、 从节点、 其余Sentinel节点的监控。
- Redis Sentinel在对节点做失败判定时分为主观下线和客观下线。
- 看懂Redis Sentinel故障转移日志对于Redis Sentnel以及问题排查非常有帮助。
- Redis Sentinel实现读写分离高可用可以依赖Sentinel节点的消息通知, 获取Redis数据节点的状态变化。
遗漏补缺:
- 领导者Sentinel节点选举 实现原理
- 故障转移 实现原理
- 故障转移日志分析