概念
redis主从模式下,有一些主节点由于故障不能提供服务,需要人工进行主从切换,将大量客户端切换到新的主节点上,但对于一定规模的应用来说,这种方案是无法接受的,所以需要有一个进程来监控主节点和从节点。
- 主节点和从节点:分别指的是redis的主服务和从服务,在物理结构上都是一个redis-server进程。
- redis数据节点:指的就是主节点和从节点。
- 哨兵节点:监控redis数据节点的节点,是一个独立的redis-sentinel进程。
- 哨兵节点集合:若干哨兵节点的抽象组合,在物理意义上就是若干个redis-sentinel进程。
- 哨兵:是哨兵节点集合和主从节点。
- 应用方:泛指一个或者多个客户端,在物理意义上是一个或者多个连接redis的进程。
恢复主节点流程
人工恢复主节点
首先看看挂了的主节点还能不能恢复,如果可以恢复的话,就不需要让从节点代替主节点,如果短时间内无法找到主节点的问题,或者知道问题但无法短时间解决的话,就会挑一个从节点,将其设置为主节点。
- 用slaveof no one命令将挑中的从节点变为主节点。
- 将其他从节点通过slaveof命令,连上新的主节点。
- 告知客户端,让客户端可以连接新的主节点,用于修改数据的操作。
哨兵机制恢复主节点

这里提供了多个redis sentinel进程,这三个哨兵节点(最好有多个哨兵节点,因为哨兵节点也是程序,也会出故障,哨兵节点少的时候,出现误判的概率会比较高)会监控现有的master和slave节点,这些进程之间,会建立tcp长连接,通过这样的长连接,定时发送心跳包。 - 当一个哨兵节点发现主节点挂了是不够的,因为此哨兵节点可能出现网络抖动的问题,需要多个哨兵节点共同认同这个事情,防止出现误判。
- 当主节点确实是出故障了,这些哨兵节点中就会推举出一个leader,由这个leader从现有的从节点中挑选一个,作为新的主节点。
- 当挑选出新的主节点后,就会自动控制被选择的节点执行slaveof no one,并且控制其他从节点,通过slaveof指令修改到新的主节点上。
- 哨兵节点会通知客户端程序,告知新的主节点是谁,后续客户端再进行写操作,就会针对新的主节点操作了。
部署
下图是我们通过docker搭建的主从结构和哨兵节点示意图:

当我们将所有程序配置在一台主机上,很容易出现配置文件/端口号/数据文件等的冲突,需要小心的避开这些冲突,docker可以看作是一个轻量级的虚拟机,使用docker,就可以解决以上问题。
- 安装docker和docker-compose。
- 停止redis服务器和redis哨兵节点。
- 使用docker获取到redis的镜像。
bash
docker pull redis:你的redis版本

- 创建6个容器,其中3个当作redis的数据节点,另外3个当作redis的哨兵节点,要先启动数据节点,再启动哨兵节点。
首先创建一个目录,里面用来存放redis数据节点和哨兵节点的目录,在每一个目录里存放一个docker-compose.yml文件。
搭建数据节点

配置完文件以后,使用以下命令启动docker容器:
bash
docker-compose up -d


搭建哨兵节点
- 在目录里创建3个目录,docker-compose.yml里配置以下内容:

bash
version: '3.8'
services:
sentinel1:
image: 'redis:6.0.16'
container_name: redis-sentinel-1
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel1:/etc/redis # 挂载目录,而不是单个文件
ports:
- "26379:26379"
networks:
- default
sentinel2:
image: 'redis:6.0.16'
container_name: redis-sentinel-2
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel2:/etc/redis # 挂载目录,而不是单个文件
ports:
- "26380:26379"
networks:
- default
sentinel3:
image: 'redis:6.0.16'
container_name: redis-sentinel-3
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel3:/etc/redis # 挂载目录,而不是单个文件
ports:
- "26381:26379"
networks:
- default
networks:
default:
external:
name: redis-data_default
-
在当前目录下创建sentinel1.conf,sentinel2.conf和sentinel3.conf文件,将下方的内容填入文件中。

-
3个redis节点和3个哨兵节点默认不在同一个局域网,所以需要把两组服务放在同一个局域网中,可以使用docker network ls查看docker局域网里的内容:

-
使用docker-compose up -d启动

-
使用docker-compose logs查看日志,如果出现以下则配置正确:

作用
使用docker ps -a可以查看到哨兵节点和数据节点都在正常工作。

- 将主节点手动停止:docker stop redis-master

- 查看哨兵节点的日志,sdown表示主观下线,仅代表本哨兵节点认为主节点挂了,odown表示客观下线,表示好几个哨兵节点认为主节点挂了,法定票数达成一致,switch表示主节点从172.19.0.2转换到172.19.0.4了。

- 查看从节点的info replication,可以看到端口号为8888的从节点已经变成了主节点了。

主从切换的流程
主观下线 :哨兵节点通过心跳包,判定redis节点是否正常工作,如果没有收到心跳包,如果能排除网络的影响,就说明redis服务器挂了。
客观下线 :多个哨兵认为主节点挂了,哨兵们就会认为这个主节点是客观下线。
选取leader节点 :让多个哨兵节点选出一个leader节点,从下图来看,1,2,3号节点都向1号节点投了一票,1号节点成为leader,由这个leader节点负责选出一个从节点作为新的主节点。

选取从节点 :选举完leader节点以后,leader就会挑选一个从节点,作为主节点。首先会判断此从节点的优先级(每个redis数据节点都会在配置文件中有一个优先级的配置,slave-priority,优先级高的从节点胜出),如果都一样,则判断offset(同步数据的进度,offset大就胜出),如果也都一样,则通过runid来挑选,也就是随机选择了。
修改:将新的主节点指定好了以后,执行slave no one成为master,再控制其他节点执行slave of,让这些其他节点以新的master作为主节点。