前言:
在分布式架构中,Redis 的高可用性(High Availability, HA)是保障业务稳定运行的关键。Redis Sentinel(哨兵集群) 作为官方推荐的 HA 解决方案,通过自动化监控、故障检测和主从切换,有效解决了单点故障问题。Sentinel 采用分布式共识机制,确保在主节点(Master)宕机时,能够快速、可靠地选举新主节点,并通知客户端更新拓扑信息,从而最小化服务中断时间。理解 Sentinel 的核心原理与最佳实践,是构建健壮 Redis 集群的重要基础。
目录
一、Redis主从复制中的问题
Redis主从复制集群可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:第一:作为主节点的一个备份,一旦主节点出了故障不能继续对外提供服务时,从节点可以作为后备"顶"上来,并且保证数据尽量不丢失。第二,从节点可以扩展主节点的读能力,通过实现读写分离结构,可以大大减轻主节点在进行高并发读写操作时的访问压力。
但是主从同步也带了一些问题:
一旦主节点故障,需要手动将一个从节点晋升为主节点。
需要修改客户端或者应用程序的主节点地址。
如果是一主多从结构,还需将其他从节点调整,让其从新的主节点进行复制而以上整个过程都需要人工干预。
二、Redis高可用方案
Redis Sentinel是Redis的高可用实现方案,在实际的生产环境中,对提高整个系统的高可用性是非常有帮助的。哨兵是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点都会对数据节点和其他Sentinel节点进行监控,当他发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,他还会和其他Sentinel节点进行"协商",当半数以上Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis的应用方。整个过程是完全自动的,不需要人工来介入,所以这套方案很有效的解决了Redis的高可用问题。
整个故障转移的处理逻辑基本上可分为4步:
主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败
每个Sentinel节点通过定期监控发现主节点出现了故障
多个Sentinel节点对主节点的故障达成一致,选举出其中一个Sentinel节点作为领导者(leader)负责本次故障转移工作。
Sentinel领导者节点执行了故障转移,如下图所示
5.故障转移后整个Redis Sentinel的拓扑关系如下所示
三、Sentinel实现原理
3.1、三个定时监控任务
Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控:
每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构,这个定时任务的作用具体可以表现在三个方面:
通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显示配置监控从节点
当有新的从节点加入时都可以立刻感知出来
节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息
2.每隔2秒,每个Sentinel节点会向Redis数据节点的sentinel:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:
发现新的Sentinel节点:通过订阅主节点的sentinel:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。
3.每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
3.2、主观下线
每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这个节点超过down-after-milliseconds时间没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个判定行为叫做主观下线。从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判断的可能。
3.3、客观下线
当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过Sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过半数的Sentinel节点都认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的。
3.4、领导者选举
故障转移的工作只需要一个Sentinel节点来完成即可,所以Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举,大致思路如下:
每个在线的Sentinel节点都有资格成为领导者,当他确认主节点主观下线时,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
收到命令的节点,如果没有同意过其他节点的请求,则会同意该请求,否则拒绝。
如果某一个节点的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
如果此过程没有选举出领导者,将进入下一次选举。
3.5、故障转移
领导者选举出的Sentinel节点负责故障转移,具体步骤如下:
在从节点列表中选出一个节点作为新的主节点,方法如下:
过滤:"不健康"(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应,与主节点失联超过down-after-milliseconds*10秒。
选择slave-priority(从优先级)最高的从节点列表,如果存在则返回,不存在则继续。
选择复制偏移量最大的从节点
选择runid最小的从节点
2.领导者节点会对第一步选出来的从节点执行slaveof no one命令,使其成为新主
3.领导者节点会向剩余的从节点发送命令,让他们成为新主的从节点
4.Sentinel节点集合会将原来的主节点更新为从节点并保持对其关注,当其恢复后命令它去复制新的主节点。
四、Sentinel集群部署
4.1、案例环境
系统 | IP地址 | 主机名 | 所需软件 | Redis角色及端口 |
---|---|---|---|---|
OpenEuler24.3 | 192.168.72.163 | master | master:6379 | |
OpenEuler24.3 | 192.168.72.164 | slave1 | slave1:6379 | |
OpenEuler24.3 | 192.168.72.165 | slave2 | salve2:6379 | |
OpenEuler24.3 | 192.168.72.163 | master | sentinel:6379 | |
OpenEuler24.3 | 192.168.72.164 | slave1 | sentinel:6379 | |
OpenEuler24.3 | 192.168.72.165 | slave2 | sentinel:6379 |
4.2、案例实施
一主两从:
主修改配置文件
[root@master ~]# vim /etc/redis-sentinel.conf
再修改redis.conf配置文件
[root@master ~]# vim /etc/redis.conf
###重启redis
[root@master ~]# systemctl restart redis
###开启哨兵服务
[root@master ~]# systemctl start redis-sentinel.service
###查看监听
[root@master ~]# netstat -anptu | grep redis
tcp 0 0 192.168.72.163:6379 0.0.0.0:* LISTEN 81498/redis-server
tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN 81831/redis-sentine
tcp 0 0 192.168.72.163:6379 192.168.72.163:47952 ESTABLISHED 81498/redis-server
tcp 0 0 192.168.72.163:6379 192.168.72.163:47936 ESTABLISHED 81498/redis-server
tcp 0 0 192.168.72.163:47936 192.168.72.163:6379 ESTABLISHED 81831/redis-sentine
tcp 0 0 192.168.72.163:47952 192.168.72.163:6379 ESTABLISHED 81831/redis-sentine
tcp6 0 0 :::26379 :::* LISTEN 81831/redis-sentine
切换到从1修改配置文件redis-sentinel.conf
[root@slave1 redis]# vim /etc/redis-sentinel.conf
从1再修改配置文件redis.conf
[root@slave1 redis]# vim /etc/redis.conf
从1开启主从复制功能
###重启redis
[root@slave1 redis]# systemctl restart redis
###开启哨兵服务
[root@slave1 redis]# systemctl start redis-sentinel.service
###查看监听
[root@slave1 redis]# netstat -anptu | grep redis
tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN 76301/redis-sentine
tcp 0 0 192.168.72.164:6379 0.0.0.0:* LISTEN 76020/redis-server
tcp 0 0 192.168.72.164:6379 192.168.72.164:47282 ESTABLISHED 76020/redis-server
tcp 0 0 192.168.72.164:59078 192.168.72.163:26379 ESTABLISHED 76301/redis-sentine
tcp 0 0 192.168.72.164:6379 192.168.72.163:49464 ESTABLISHED 76020/redis-server
tcp 0 0 192.168.72.164:55132 192.168.72.163:6379 ESTABLISHED 76301/redis-sentine
tcp 0 0 192.168.72.164:6379 192.168.72.164:47266 ESTABLISHED 76020/redis-server
tcp 0 0 192.168.72.164:58884 192.168.72.163:6379 ESTABLISHED 76020/redis-server
tcp 0 0 192.168.72.164:6379 192.168.72.163:49458 ESTABLISHED 76020/redis-server
tcp 0 0 192.168.72.164:47282 192.168.72.164:6379 ESTABLISHED 76301/redis-sentine
tcp 0 0 192.168.72.164:26379 192.168.72.163:52078 ESTABLISHED 76301/redis-sentine
tcp 0 0 192.168.72.164:47266 192.168.72.164:6379 ESTABLISHED 76301/redis-sentine
tcp 0 0 192.168.72.164:55130 192.168.72.163:6379 ESTABLISHED 76301/redis-sentine
tcp6 0 0 :::26379 :::* LISTEN 76301/redis-sentine
切换到从2修改配置文件redis-sentinel.conf
[root@slave2 redis]# vim /etc/redis-sentinel.conf
从2再修改配置文件redis.conf
[root@slave2 redis]# vim /etc/redis.conf
从2开启主从复制功能
###重启redis
[root@slave2 redis]# systemctl restart redis
###开启哨兵服务
[root@slave2 redis]# systemctl start redis-sentinel.service
###查看监听
[root@slave2 redis]# netstat -anptu | grep redis
tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN 45886/redis-sentine
tcp 0 0 192.168.72.165:6379 0.0.0.0:* LISTEN 45824/redis-server
tcp 0 0 192.168.72.165:45950 192.168.72.163:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:49394 192.168.72.164:26379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:47368 192.168.72.164:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:6379 192.168.72.165:41920 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:37262 192.168.72.163:6379 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:41926 192.168.72.165:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:41920 192.168.72.165:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:47354 192.168.72.164:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:6379 192.168.72.164:46212 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:26379 192.168.72.163:53022 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:26379 192.168.72.164:52840 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:6379 192.168.72.163:42900 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:6379 192.168.72.163:42910 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:6379 192.168.72.165:41926 ESTABLISHED 45824/redis-server
tcp 0 0 192.168.72.165:46060 192.168.72.163:26379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:45944 192.168.72.163:6379 ESTABLISHED 45886/redis-sentine
tcp 0 0 192.168.72.165:6379 192.168.72.164:46218 ESTABLISHED 45824/redis-server
tcp6 0 0 :::26379 :::* LISTEN 45886/redis-sentine
此时主的redis-sentinel.conf配置文件中最底下会添加一条监控主的命令(当主重启或down就会执行操作)
故障模拟:
当主停止redis时
[root@master ~]# systemctl stop redis
此时切换到从2登录redis输入info查看到从2变为了主
[root@slave1 redis]# redis-cli -h 192.168.72.164
192.168.72.164:6379> info
切换到从1登录redis输入info查看到到从1的 主 变成了 原先192.168.72.165从2
[root@slave2 redis]# redis-cli -h 192.168.72.165
192.168.72.165:6379> info
此时原先主192.168.72.163 master 登录redis 输入info查看详细信息
[root@master ~]# systemctl start redis
[root@master ~]# redis-cli -h 192.168.72.163 -a 123.com
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
AUTH failed: ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct?
192.168.72.163:6379> info
查看到现主变成了192.168.72.165
切换到现主192.168.72.165上再次输入info查看详细信息,原先从2 正常管理原先主192.168.72.163和从1 192.168.72.164
此时切换到原先主192.168.72.163配置文件redis.conf中查看到配置未修改
[root@master ~]# vim /etc/redis.conf
查看哨兵配置文件redis-sentinel.conf中查看到配置也没有进行修改
注意:早期的 Sentinel 实现会在主从切换时直接修改 Redis 节点的配置文件(redis.conf
),但新版本(Redis 5.0+)已优化这一行为,转而通过动态命令和内存状态管理集群拓扑,避免了配置文件的频繁写入,提升了可靠性和运维便利性。
此时切换到现主192.168.72.166停止redis
[root@slave2 redis]# systemctl stop redis
切换到192.168.72.164从1 发现变成了新主
此时我们得到sentinel基于ID进行选举的验证192.168.72.164的ID:cd大于192.168.72.163 的ID:8f。
总结:
Redis Sentinel 通过多实例协作的哨兵集群,实现了主从架构的自动化故障转移,显著提升了系统的可用性。其基于 Raft 的选举机制和主观/客观下线判定策略,平衡了可靠性与响应速度。然而,Sentinel 本身仍需依赖合理部署(如至少 3 节点防脑裂)和客户端适配(如重定向逻辑)。未来,结合 Redis Cluster 或 Proxy 方案可进一步扩展规模,但 Sentinel 仍是中小规模场景下简单高效的 HA 选择。