Redis 的主从复制模式下,⼀旦主节点由于故障不能提供服务,需要人工进行主从切换,同时大量 的客户端需要被通知切换到新的主节点上,对于上了⼀定规模的应⽤来说,这种⽅案是⽆法接受的, 于是 Redis 从 2.8 开始提供了 Redis Sentinel(哨兵)来自动化解决这个问题。
由于对 Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel 之前,先对⼏个名词 概念进⾏必要的说明,如表所⽰。
|--------------------|------------------|-------------------------|
| 名词 | 逻辑结构 | 物理结构 |
| 主节点 | Redis 主服务 | ⼀个独⽴的 redis-server 进程 |
| 从节点 | Redis 从服务 | ⼀个独⽴的 redis-server 进程 |
| Redis 数据节点 | 主从节点 | 主节点和从节点的进程 |
| 哨兵节点 | 监控 Redis 数据节点的节点 | ⼀个独⽴的 redis-sentinel 进程 |
| 哨兵节点集合 | 若⼲哨兵节点的抽象组合 | 若⼲ redis-sentinel 进程 |
| Redis 哨兵(Sentinel) | Redis 提供的高可用方案 | 哨兵节点集合 和 Redis 主从节点 |
| 应⽤⽅ | 泛指⼀个多多个客⼾端 | ⼀个或多个连接 Redis 的进程 |
哨兵机制,是通过独立的 进程 来体现的。和之前 redis-server 是不同的进程!
redis-sentinel 不负责存储数据,只是对其他的 redis-server 进程起到监控的效果~~
通常哨兵节点,也会搞一个集合~~ (多个哨兵节点构成的),防止单个哨兵节点
主从复制的问题
Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作⽤: 第⼀,作为主节点的⼀个备份,⼀旦主节点出了故障不可达的情况,从节点可以作为后备 "顶" 上 来,并且保证数据尽量不丢失(主从复制表现为最终⼀致性)。第⼆,从节点可以分担主节点上的读 压⼒,让主节点只承担写请求的处理,将所有的读请求负载均衡到各个从节点上。 但是主从复制模式并不是万能的,它同样遗留下以下⼏个问题:
-
主节点发生故障时,进行主从切换的过程是复杂的,需要完全的人工参与,导致故障恢复时间⽆法 保障。
-
主节点可以将读压⼒分散出去,但写压⼒/存储压⼒是⽆法被分担的,还是受到单机的限制。 其中第⼀个问题是高可用问题,即 Redis 哨兵主要解决的问题。第⼆个问题是属于存储分布式的问 题,留给 Redis 集群去解决。
人工操作
Redis 主节点故障后需要进⾏的人工操作

1)运维⼈员通过监控系统,发现 Redis 主节点故障宕机。
2)运维⼈员从所有节点中,选择⼀个(此处选择了 slave 1)执行 slaveof no one,使其作为新的主 节点。
3)运维⼈员让剩余从节点(此处为 slave 2)执行 slaveof {newMasterIp} {newMasterPort} 从新主节 点开始数据同步。
4)更新应用方连接的主节点信息到 {newMasterIp} {newMasterPort}。
5)如果原来的主节点恢复,执行 slaveof {newMasterIp} {newMasterPort} 让其成为⼀个从节点。
这样的人工干预,还是比较繁琐的。
其次,人工干预的过程出现操作不当,数据丢失,也是很严重的。
还有人工干预也需要时间消耗。
哨兵自动恢复主节点故障
当主节点出现故障时,Redis Sentinel 能⾃动完成故障发现和故障转移,并通知应⽤⽅,从⽽实现 真正的⾼可⽤。
Redis Sentinel 是⼀个分布式架构,其中包含若⼲个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进⾏监控,当它发现节点不可达时,会对节点做下线表⽰。如果下线的是主节点,它还会和其他的 Sentinel 节点进⾏ "协商",当⼤多数 Sentinel 节点对 主节点不可达这个结论达成共识之后,它们会在内部 "选举" 出⼀个领导节点来完成⾃动故障转移的 ⼯作,同时将这个变化实时通知给 Redis 应用方。整个过程是完全⾃动的,不需要⼈⼯介⼊。整体的 架构如图所示。

监控:这些进程之间,会建立tcp长连接,通过这样的长连接,定期发送心跳包。
Redis Sentinel 相⽐于主从复制模式是多了若干(建议保持奇数)Sentinel 节点⽤于实现监控数据节 点,哨兵节点会定期监控所有节点(包含数据节点和其他哨兵节点)。针对主节点故障的情况,故障 转移流程⼤致如下:
1)主节点故障,从节点同步连接中断,主从复制停⽌。
2)哨兵节点通过定期监控发现主节点出现故障。哨兵节点与其他哨兵节点进行协商 ,达成多数认同主 节点故障的共识。这步主要是防⽌该情况:出故障的不是主节点,⽽是发现故障的哨兵节点,该情况 经常发生于哨兵节点的网络被孤⽴的场景下。
3)哨兵节点之间使⽤ Raft 算法选举出⼀个领导角色,由该节点负责后续的故障转移⼯作。
4)哨兵领导者开始执⾏故障转移:从节点中选择⼀个作为新主节点,执行 slaveof no one;让其他从节点同步新主节点,修改 slaveof 到新的主节点上;通 知应用层转移到新主节点。
通过上⾯的介绍,可以看出 Redis Sentinel 具有以下几个功能:
• 监控: Sentinel 节点会定期检测 Redis 数据节点、其余哨兵节点是否可达。
• 故障转移: 实现从节点晋升(promotion)为主节点并维护后续正确的主从关系。
• 通知: Sentinel 节点会将故障转移的结果通知给应用方。
注意, redis 哨兵节点,有一个,也是可以的。但是,1、如果哨兵节点只有一个,它自身也是容易出现问题的,万一这个哨兵节点挂了,后续redis节点也挂了,就无法进行自动的恢复过程了。2、出现误判的概率也比较高,毕竟网络传数据是容易出现抖动或者延迟或者丢包的,如果只有一个哨兵节点,出现上述问题之后,影响就比较大。
因此在分布式系统中,应该避免使用"单点",哨兵节点,最好要设置奇数个(方便后续的选举),最少也应该是3个。
安装部署
目前搭建的拓扑如下

但是目前我只有一台云服务器,所以使用docker虚拟化来模拟这样的拓扑结构。
1、拉取redis镜像,执行命令 docker pull redis:5.0.9
准备好如下目录

2、在redis-data目录下编写docker-compose.yml文件编排redis主从节点
bash
#version: '3.7'
services:
master:
image: 'redis:5.0.9'
container_name: redis-master
restart: always
command: redis-server --appendonly yes
ports:
- 6379:6379
slave1:
image: 'redis:5.0.9'
container_name: redis-slave1
restart: always
command: redis-server --appendonly yes --slaveof redis-master 6379
ports:
- 8081:6379
slave2:
image: 'redis:5.0.9'
container_name: redis-slave2
restart: always
command: redis-server --appendonly yes --slaveof redis-master 6379
ports:
- 8082:6379
在执行命令创建容器 docker compose up -d
在查看(docker ps -a | grep redis), 容器就启起来了

3、在rredis-sentinel目录下编写docker-compose.yml文件编排哨兵节点
bash
#version: '3.7'
services:
sentinel1:
image: 'redis:5.0.9'
container_name: redis-sentinel-1
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel1.conf:/etc/redis/sentinel.conf
ports:
- 8083:26379
sentinel2:
image: 'redis:5.0.9'
container_name: redis-sentinel-2
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel2.conf:/etc/redis/sentinel.conf
ports:
- 8084:26379
sentinel3:
image: 'redis:5.0.9'
container_name: redis-sentinel-3
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/etc/redis/sentinel.conf
ports:
- 8085:26379
networks:
default:
name: redis-data_default
external: true
然后在当前目录下新建sentinel1.conf、sentinel2.conf、sentinel3.conf配置文件
首先编写sentinel1.conf文件

sentinel2.conf和sentinel3.conf类似配置,初始情况,这三个文件可以是一样的,随着运行哨兵节点会自适应调整配置的内容。
然后启动容器,执行命令 docker compose up -d
然后再查看容器

目前,redis主从节点和哨兵节点就部署好了
然后再次查看sentinel1.conf文件,发现配置文件就不一样了,这就是哨兵的配置重写

哨兵节点的作用演示
哨兵存在的意义,能够在redis 主从结构出现问题的时候(比如主节点挂了),此时哨兵节点就能够自动的帮我们重新选出一个主节点,来代替之前挂了的节点.保证整个redis 仍然是可用状态

目前redis主从节点和哨兵节点都是正常运行的。
模拟主节点挂了的情况,将redis-master停掉,执行命令 docker stop redis-master
然后查看日志,执行命令 docker compose logs

然后登录8081从节点,观察是否调整为主节点

然后登录8081从节点,观察

主从切换的具体流程
1.主观下线
哨兵节点通过心跳包,判定 redis 服务器是否正常工作.如果心跳包没有如约而至,就说明 redis 服务器挂了。
此时还不能排除网络波动的影响,因此就只能是单方面认为这个redis 节点挂了
2.客观下线
多个哨兵都认为主节点挂了,(认为挂了的哨兵节点数目达到法定票数)哨兵们就认为这个主节点是客观下线.
3.要让多个哨兵节点,选出一个 leader 节点(多个哨兵节点投票选举一个哨兵节点出来)
由这个leader负责选一个从节点作为新的主节点,这个投票过程,主要看谁的网络时延小。
- 此时leader 选举完毕,leader 就需要挑选一个从节点,作为新的主节点。
-
优先级 每个 redis 数据节点,都会在配置文件中,有一个优先级的设置。 slave-priority
优先级高的从节点,就会胜出。(当没有修改,使用默认优先级,就是大家都是一样的) -
offset 最大, 就胜出。offset表示从节点从主节点这边同步数据的进度。数值越大,说明从节点的数据和主节点就越接近。
-
run id 是在每个 redis 节点启动的时候随机生成的一串数字,此时选谁都行,全凭缘分了。
把新的主节点指定好了之后,leader 就会控制这个这个节点,执行 slave no one ,成为 master,
再控制其他节点,执行 slave of,让这些其他节点,以新的 master 作为主节点了。
注意事项:
• 哨兵节点不能只有⼀个, 否则哨兵节点挂了也会影响系统可⽤性。
• 哨兵节点最好是奇数个, ⽅便选举 leader, 得票更容易超过半数。
• 哨兵节点不负责存储数据, 仍然是 redis 主从节点负责存储。所以哨兵节点可以使用一些配置不高的机器来部署(实际场景下,不能部署在一台机器上)。
• 哨兵 + 主从复制解决的问题是 "提高可用性", 不能解决 "数据极端情况下写丢失" 的问题。
• 哨兵 + 主从复制不能提⾼数据的存储容量, 当我们需要存的数据接近或者超过机器的物理内存, 这样 的结构就难以胜任了。
为了能存储更多的数据, 就引⼊了集群