一、前言
redis 主从复制模式下, 一旦主节点出现故障, 不能提供服务的时候, 就需要人工进行主从切换. 这是十分不靠谱的, 毕竟谁也不知道主节点什么时候出现故障, 这样就无法第一时间恢复服务. 所以 redis 从 2.8 版本开始提供了 哨兵(Redis Sentinel) 模式, 可以通过自动化手段来解决主节点出现故障的问题.进而实现故障转移。
二、主从复制的问题
redis 主从复制是将主节点的数据同步给从节点, 这样从节点就拥有主节点的所有数据, 这样客户端就可以从从节点这边来读取数据, 来分担主节点所要承受的并发量. 但是主从复制也有一些问题:
问题一:主节点发生故障的时候, 这时整个 redis 服务就失去了写入数据的功能, 这时就需要程序员手动干预来将从节点切换成主节点.
问题二:主从复制可以将读的并发量分担给从节点, 但是写压力/存储压力是无法被分担的, 还是受到单机的限制.
其中第一个问题就是哨兵模式主要解决的问题, 而第二个问题是 redis 集群模式解决的问题, 本章主要讨论第一个问题.
三、哨兵的原理&故障转移过程
(1)定时任务:每个哨兵节点维护了3个定时任务。定时任务的功能分别如下:通过向主从节点发送info命令获取最新的主从结构;通过发布订阅功能获取其他哨兵节点的信息;通过向其他节点发送ping命令进行心跳检测,判断是否下线。
(2)主观下线:在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。顾名思义,主观下线的意思是一个哨兵节点“主观地”判断下线;与主观下线相对应的是客观下线。
(3)客观下线:哨兵节点在对主节点进行主观下线后,会通过sentinel is-master-down-by-addr命令询问其他哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。
(4)选举领导者哨兵节点:当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。
监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为领导者的申请,如果B没有同意过其他哨兵,则会同意A成为领导者。选举的具体过程这里不做详细描述,一般来说,哨兵选择的过程很快,谁先完成客观下线,一般就能成为领导者。
(5)故障转移:选举出的领导者哨兵,开始进行故障转移操作,该操作大体可以分为3个步骤:
- 在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点;然后选择优先级最高的从节点(由slave-priority指定);如果优先级无法区分,则选择复制偏移量最大的从节点;如果仍无法区分,则选择runid最小的从节点。
- 更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
- 将已经下线的主节点(即6379)设置为新的主节点的从节点,当6379重新上线后,它会成为新的主节点的从节点。
- 通知:哨兵可以将故障转移的结果发送给客户端
四、实践案例:
用docker来模拟主从及哨兵模式:
1、搭建redis主从服务 redis.yml:
version: '2.0'
services:
redis-master:
image: redis
container_name: redis-master
ports:
- "6380:6379"
networks:
- redis-net
command: redis-server --port 6379
redis-slave-1:
image: redis
container_name: redis-slave-1
ports:
- "6381:6379"
networks:
- redis-net
command: redis-server --slaveof redis-master 6379 --port 6379
redis-slave-2:
image: redis
container_name: redis-slave-2
ports:
- "6382:6379"
networks:
- redis-net
command: redis-server --slaveof redis-master 6379 --port 6379
networks:
redis-net:
external: true
2、搭建redis哨兵 sentinel.yml:
哨兵个数为大于等于3等奇数个。
version: '2.0'
services:
redis-sentinel-1:
image: redis:5.0.9
container_name: redis-sentinel-1
restart: always
ports:
- "26379:26379"
networks:
- redis-net
volumes:
- ./sentinel.conf:/etc/redis/sentinel.conf
command: redis-sentinel /etc/redis/sentinel.conf
redis-sentinel-2:
image: redis:5.0.9
container_name: redis-sentinel-2
ports:
- "26380:26379"
networks:
- redis-net
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel1.conf:/etc/redis/sentinel.conf
redis-sentinel-3:
image: redis:5.0.9
container_name: redis-sentinel-3
ports:
- "26381:26379"
networks:
- redis-net
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel2.conf:/etc/redis/sentinel.conf
networks:
redis-net:
external: true
三个sentinel.conf文件一样,如下:
bind 0.0.0.0
port 26379
sentinel monitor mymaster redis-master 6379 2
它告诉 Sentinel 监视名为 mymaster 的主节点,地址为 redis-master,该主节点监听在 6379 端口上。最后一个参数 2 表示在 Sentinel 发现主节点失效后,至少需要 2 个 Sentinel 同意进行故障转移,才会执行故障转移操作。
启动服务:
docker network create redis-net 先创建一个外部网络环境
docker-compose -f redis.yml up 启动reids主从服务
docker-compose -f sentinel.yml up 启动哨兵服务
主从日志:
哨兵日志:
哨兵启动后,会自动修改配置文件如下:
bind 0.0.0.0 # 将 Sentinel 服务绑定到所有网络接口 port 26379 # Sentinel 服务监听的端口号 sentinel myid 5cc2477171288ff3dbaf1db764520983ca75e60c # Sentinel 服务的唯一标识符 # Generated by CONFIG REWRITE dir "/data" # Sentinel 的工作目录,用于存储运行时数据 sentinel deny-scripts-reconfig yes # 禁止 Sentinel 在运行时重新加载脚本,以增强安全性 sentinel monitor mymaster 172.28.0.4 6379 2 # 配置 Sentinel 监视名为 mymaster 的主节点,要求至少 2 个 Sentinel 同意进行故障转移,172.28.0.4是主节点容器ip sentinel config-epoch mymaster 0 # 主节点 mymaster 的配置纪元 sentinel leader-epoch mymaster 0 # 主节点 mymaster 的领导者纪元 sentinel known-replica mymaster 172.28.0.2 6379 # 主节点 mymaster 已知的从节点列表 sentinel known-replica mymaster 172.28.0.3 6379 # 主节点 mymaster 已知的从节点列表 sentinel known-sentinel mymaster 172.28.0.7 26379 1fb0c93185eeb00e37c2b222d9dd069e433387a2 # 主节点 mymaster 已知的其他 Sentinel 节点列表 sentinel known-sentinel mymaster 172.28.0.5 26379 7b4fa92be37276cce79d770e2d7afeb5f67bf9e2 # 主节点 mymaster 已知的其他 Sentinel 节点列表 sentinel current-epoch 0 # 当前的 Sentinel 集群纪元(以上ip都是容器内服务ip)
模拟主节点故障:
docker stop redis-master
哨兵日志:
自动修改后的哨兵配置文件:
bind 0.0.0.0 port 26379 sentinel myid 5cc2477171288ff3dbaf1db764520983ca75e60c # Generated by CONFIG REWRITE dir "/data" sentinel deny-scripts-reconfig yes sentinel monitor mymaster 172.28.0.2 6379 2 主节点由172.28.0.4切换为172.28.0.2 sentinel config-epoch mymaster 1 sentinel leader-epoch mymaster 1 sentinel known-replica mymaster 172.28.0.3 6379 sentinel known-replica mymaster 172.28.0.4 6379 sentinel known-sentinel mymaster 172.28.0.7 26379 1fb0c93185eeb00e37c2b222d9dd069e433387a2 sentinel known-sentinel mymaster 172.28.0.5 26379 7b4fa92be37276cce79d770e2d7afeb5f67bf9e2 sentinel current-epoch 1
哨兵模式需要客户端配合才能达到预期的故障转移效果,因为故障转移后,主节点ip会发生变化,客户端通常需要监听哨兵通知,做出相应动作调整连接信息。