文章目录
-
- [Redis 哨兵(Sentinel):高可用自动故障转移](#Redis 哨兵(Sentinel):高可用自动故障转移)
- 一、前言
- 二、主从复制的遗留问题
-
- [2.1 主从复制能解决什么](#2.1 主从复制能解决什么)
- [2.2 主从复制留下的两个核心问题](#2.2 主从复制留下的两个核心问题)
- 三、哨兵是什么
- [四、安装部署(基于 Docker)](#四、安装部署(基于 Docker))
-
- [4.1 Redis 5.0 与 Docker 主机名的注意事项](#4.1 Redis 5.0 与 Docker 主机名的注意事项)
- [4.2 准备工作](#4.2 准备工作)
- [4.3 编排 Redis 主从节点](#4.3 编排 Redis 主从节点)
- [4.4 验证 Redis 主从关系](#4.4 验证 Redis 主从关系)
- [4.5 编排 Sentinel 节点](#4.5 编排 Sentinel 节点)
- [4.6 Sentinel 配置项说明](#4.6 Sentinel 配置项说明)
-
- [sentinel monitor](#sentinel monitor)
- [sentinel down-after-milliseconds](#sentinel down-after-milliseconds)
- [sentinel failover-timeout](#sentinel failover-timeout)
- [sentinel parallel-syncs](#sentinel parallel-syncs)
- [4.7 启动 Sentinel 节点](#4.7 启动 Sentinel 节点)
- [4.8 观察 Sentinel 配置自动 rewrite](#4.8 观察 Sentinel 配置自动 rewrite)
- [五、Sentinel 常用命令](#五、Sentinel 常用命令)
-
- [5.1 查看所有主节点](#5.1 查看所有主节点)
- [5.2 查看指定主节点信息](#5.2 查看指定主节点信息)
- [5.3 查看从节点信息](#5.3 查看从节点信息)
- [5.4 查看其他 Sentinel 节点](#5.4 查看其他 Sentinel 节点)
- [5.5 查询当前主节点地址](#5.5 查询当前主节点地址)
- [5.6 手动触发故障转移](#5.6 手动触发故障转移)
- [六、客户端如何接入 Sentinel](#六、客户端如何接入 Sentinel)
- 七、故障转移验证
- 八、选举原理(核心重点)
-
- [8.1 主观下线(SDown)](#8.1 主观下线(SDown))
- [8.2 客观下线(ODown)](#8.2 客观下线(ODown))
- [8.3 Leader 选举(类 Raft 的多数派选举)](#8.3 Leader 选举(类 Raft 的多数派选举))
-
- 第一步:发起拉票
- 第二步:收到请求后投票
- [第三步:获得多数票成为 Leader](#第三步:获得多数票成为 Leader)
- [8.4 为什么建议 Sentinel 节点是奇数个](#8.4 为什么建议 Sentinel 节点是奇数个)
- [8.5 Leader 如何选择新的主节点](#8.5 Leader 如何选择新的主节点)
-
- 第一步:过滤不合格从节点
- 第二步:按优先级排序
- [第三步:比较复制偏移量 offset](#第三步:比较复制偏移量 offset)
- [第四步:比较 run ID](#第四步:比较 run ID)
- [8.6 Leader 执行故障转移](#8.6 Leader 执行故障转移)
-
- 第一步:让目标从节点晋升为主节点
- 第二步:让其他从节点复制新主节点
- [第三步:更新 Sentinel 内部配置](#第三步:更新 Sentinel 内部配置)
- 第四步:原主节点恢复后改造成从节点
- [九、Sentinel 的局限性](#九、Sentinel 的局限性)
-
- [9.1 Sentinel 不保证零数据丢失](#9.1 Sentinel 不保证零数据丢失)
- [9.2 Sentinel 不能解决存储容量问题](#9.2 Sentinel 不能解决存储容量问题)
- [9.3 Sentinel 对客户端有要求](#9.3 Sentinel 对客户端有要求)
- [9.4 Docker / NAT 环境要注意地址问题](#9.4 Docker / NAT 环境要注意地址问题)
- 十、总结
-
- [10.1 Sentinel 解决的问题](#10.1 Sentinel 解决的问题)
- [10.2 Sentinel 的三个核心功能](#10.2 Sentinel 的三个核心功能)
- [10.3 主观下线和客观下线](#10.3 主观下线和客观下线)
- [10.4 quorum 和 majority 的区别](#10.4 quorum 和 majority 的区别)
- [10.5 Leader 选举](#10.5 Leader 选举)
- [10.6 新主节点选择规则](#10.6 新主节点选择规则)
- [10.7 Sentinel 的注意事项](#10.7 Sentinel 的注意事项)
Redis 哨兵(Sentinel):高可用自动故障转移
一、前言
💬 这一篇讲什么:Redis 哨兵机制
🚀 核心内容:
- 主从复制遗留了什么问题?为什么需要哨兵?
- 哨兵是什么?整体架构是怎样的?
- 哨兵如何部署?有哪些常用命令?
- 客户端如何通过哨兵找到真正的主节点?
- 哨兵如何发现主节点故障?
- 主观下线和客观下线是什么?
- 哨兵 Leader 是如何选举出来的?
- Leader 按什么标准挑选新的主节点?
⚠️ 注意 :本章部署相关的操作不需要死记,工作中如果用到了能查到即可。重点是理解流程和原理。
上一篇讲完了 Redis 主从复制。主从复制让 Redis 有了数据冗余和读写分离能力,但它仍然有一个非常明显的问题:主节点挂了之后,需要人工介入才能恢复服务。
比如:
- 人工判断主节点是否真的宕机;
- 人工从从节点里选一个新主节点;
- 人工让其他从节点跟随新主节点;
- 人工通知应用切换 Redis 地址;
- 原主节点恢复后,还要人工把它挂到新主节点下面。
对于线上系统来说,这种方式恢复太慢,也太依赖人工操作,不能算真正的高可用。
Redis 从 2.8 开始提供了 Redis Sentinel,也就是哨兵机制。哨兵就是用来解决主从复制下主节点故障后自动发现、自动切换、自动通知的问题。
还可以继续用上一篇主从复制里的例子理解:
主从复制中,主节点就像授课老师,从节点就像助教老师。助教可以从老师那里同步知识,也可以帮助学生答疑。但是如果老师突然请假了,助教虽然有知识,却不会自动站上讲台继续上课。
哨兵的作用就是:
text
发现老师请假了
↓
判断这不是误会
↓
从助教中选一个代课老师
↓
通知其他助教跟随新的代课老师
↓
通知学生以后找新的老师上课
二、主从复制的遗留问题
2.1 主从复制能解决什么
Redis 主从复制可以把主节点的数据变化同步给从节点。
从节点主要有两个作用。
第一,作为主节点的数据副本。
如果主节点故障,从节点里还有数据,可以作为后续故障恢复的基础。
第二,分担读压力。
主节点主要处理写请求,从节点可以处理读请求。对于读多写少的场景,可以通过多个从节点分担读压力。
text
客户端写请求 ─────→ 主节点 Master
客户端读请求 ─────→ 从节点 Replica 1
客户端读请求 ─────→ 从节点 Replica 2
客户端读请求 ─────→ 从节点 Replica 3
但是主从复制并不是万能的。
2.2 主从复制留下的两个核心问题
主从复制主要留下两个问题。
问题一:高可用问题
主节点一旦故障,主从复制本身不会自动完成主备切换。
也就是说,从节点不会自动变成主节点。
如果没有哨兵,主节点宕机后通常需要人工处理:
text
发现主节点宕机
↓
选择一个从节点
↓
执行 SLAVEOF NO ONE
↓
让其他从节点复制新的主节点
↓
修改应用连接配置
这个过程恢复时间不可控,不能满足高可用要求。
这个问题就是 Redis Sentinel 要解决的问题。
问题二:存储容量问题
主从复制虽然可以分担读压力,但是写请求仍然只能落到主节点,数据也仍然是一整份完整数据。
也就是说:
text
主节点有 100GB 数据
从节点也要有 100GB 数据
主从复制不能把数据拆开存储到多台机器上,所以它不能突破单机内存容量限制。
这个问题不是哨兵解决的,而是 Redis Cluster 要解决的。
本章只讨论第一个问题:高可用自动故障转移。
三、哨兵是什么
3.1 基本概念
在正式讲 Sentinel 之前,先把几个名词说清楚。
| 名词 | 逻辑结构 | 物理结构 |
|---|---|---|
| 主节点 | Redis 主服务 | 一个独立的 redis-server 进程 |
| 从节点 | Redis 从服务 | 一个独立的 redis-server 进程 |
| Redis 数据节点 | 主节点和从节点 | 存储真实数据的 Redis 进程 |
| 哨兵节点 | 监控 Redis 数据节点的节点 | 一个独立的 redis-sentinel 进程 |
| 哨兵节点集合 | 多个哨兵节点的组合 | 多个 redis-sentinel 进程 |
| Redis Sentinel | Redis 提供的高可用方案 | 哨兵节点集合 + Redis 主从节点 |
| 应用方 | 使用 Redis 的客户端 | 一个或多个连接 Redis 的进程 |
注意:哨兵节点本身不存储业务数据。真正存储数据的仍然是 Redis 主从节点。
3.2 哨兵的整体架构
Redis Sentinel 是一个分布式高可用架构,由 Redis 数据节点和 Sentinel 节点共同组成。
text
Sentinel 节点集合
sentinel1 sentinel2 sentinel3
│ │ │
└──────────┼──────────┘
│
监控 Redis 主从
│
┌────────────────┴────────────────┐
│ │
Master ───────────── 主从复制 ─────→ Replica1
│
└───────────────────────────────→ Replica2
每个 Sentinel 节点都会定期监控:
- Redis 主节点;
- Redis 从节点;
- 其他 Sentinel 节点。
如果某个 Sentinel 发现主节点不可达,它不会马上执行故障转移,而是先把主节点标记为主观下线。
之后,多个 Sentinel 节点会互相确认。如果达到配置的法定票数,主节点会被标记为客观下线。
确认主节点客观下线后,Sentinel 节点之间会选举出一个 Leader,由这个 Leader 负责执行故障转移:
text
选择一个从节点成为新主节点
↓
让其他从节点复制新主节点
↓
把原主节点恢复后改造成从节点
↓
通知客户端主节点已经发生切换
3.3 哨兵的三个核心功能
Redis Sentinel 主要有三个核心功能。
1)监控
Sentinel 会定期检测 Redis 主节点、从节点以及其他 Sentinel 节点是否可达。
如果节点长时间没有响应,Sentinel 会认为该节点可能出现故障。
2)故障转移
当主节点被确认故障后,Sentinel 会自动从从节点中选择一个新的主节点,并让其他从节点复制新的主节点。
这个过程叫:
text
failover
也就是故障转移。
3)通知
Sentinel 会将故障转移结果通知给客户端。
不过要注意:Sentinel 不是直接修改应用配置文件。应用程序需要使用支持 Sentinel 的客户端,由客户端向 Sentinel 查询当前主节点地址,并在故障转移后自动切换连接。
四、安装部署(基于 Docker)
⚠️ 本节主要用于本地学习和观察日志,不建议把这里的 Docker Compose 配置原封不动用于生产环境。
Redis Sentinel 在 Docker、NAT、端口映射环境中容易遇到地址不可达问题。生产环境中必须保证 Sentinel、Redis 数据节点、客户端看到的地址都是彼此可达的真实地址。
4.1 Redis 5.0 与 Docker 主机名的注意事项
本系列使用的是 Redis 5.0。
在 Docker Compose 里,容器之间可以通过服务名或容器名互相访问,比如:
text
redis-master
redis-slave1
redis-slave2
所以有些资料会直接写:
bash
sentinel monitor redis-master redis-master 6379 2
在部分环境中,这样可以被 Docker DNS 解析成功。
但是 Redis 5.0 的 Sentinel 对主机名支持并不完整,Sentinel 运行过程中还会自动 rewrite 配置文件,经常会把主机名改写成容器内部 IP。
所以为了让 Redis 5.0 的示例更稳定,本篇使用固定容器 IP 演示 Sentinel。
如果使用 Redis 6.2 及以上版本,可以了解 resolve-hostnames、announce-hostnames 等配置,但本篇仍然按 Redis 5.0 系列来讲。
4.2 准备工作
安装 Docker 和 docker-compose。
Ubuntu:
bash
apt install docker-compose
CentOS:
bash
yum install docker-compose
拉取 Redis 5.0.9 镜像:
bash
docker pull redis:5.0.9
如果本机之前已经启动了 Redis,可以先停止:
bash
service redis-server stop
service redis-sentinel stop
4.3 编排 Redis 主从节点
创建目录:
bash
mkdir -p /root/redis
cd /root/redis
创建 docker-compose.yml:
yaml
version: '3.7'
services:
master:
image: 'redis:5.0.9'
container_name: redis-master
restart: always
command: redis-server --appendonly yes
ports:
- 6379:6379
networks:
redis-net:
ipv4_address: 172.22.0.2
slave1:
image: 'redis:5.0.9'
container_name: redis-slave1
restart: always
command: redis-server --appendonly yes --slaveof 172.22.0.2 6379
ports:
- 6380:6379
networks:
redis-net:
ipv4_address: 172.22.0.3
slave2:
image: 'redis:5.0.9'
container_name: redis-slave2
restart: always
command: redis-server --appendonly yes --slaveof 172.22.0.2 6379
ports:
- 6381:6379
networks:
redis-net:
ipv4_address: 172.22.0.4
networks:
redis-net:
name: redis-sentinel-net
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/16
启动容器:
bash
docker-compose up -d
查看日志:
bash
docker-compose logs
如果配置有误,可以停止并删除容器:
bash
docker-compose down
4.4 验证 Redis 主从关系
连接主节点:
bash
redis-cli -p 6379
执行:
bash
127.0.0.1:6379> INFO replication
可以看到类似信息:
text
# Replication
role:master
connected_slaves:2
slave0:ip=172.22.0.3,port=6379,state=online,offset=348,lag=1
slave1:ip=172.22.0.4,port=6379,state=online,offset=348,lag=1
master_replid:a22196b425ab42ddfd222cc5a64d53acffeb3e63
master_repl_offset:348
连接从节点:
bash
redis-cli -p 6380
执行:
bash
127.0.0.1:6380> INFO replication
可以看到类似信息:
text
# Replication
role:slave
master_host:172.22.0.2
master_port:6379
master_link_status:up
slave_repl_offset:446
slave_read_only:1
另一个从节点:
bash
redis-cli -p 6381
也应该能看到:
text
role:slave
master_host:172.22.0.2
master_port:6379
master_link_status:up
4.5 编排 Sentinel 节点
Sentinel 可以和 Redis 主从节点写在同一个 docker-compose.yml 中。
这里分成两个目录,主要是为了:
- 方便单独观察 Sentinel 日志;
- 确保 Redis 主从节点先启动;
- 避免一开始就触发不必要的故障转移日志,影响学习观察。
创建目录:
bash
mkdir -p /root/redis-sentinel
cd /root/redis-sentinel
创建三个 Sentinel 配置文件:
bash
touch sentinel1.conf sentinel2.conf sentinel3.conf
三份文件内容基本相同。
sentinel1.conf:
bash
bind 0.0.0.0
port 26379
sentinel monitor redis-master 172.22.0.2 6379 2
# 本地演示为了快速观察故障转移,设置为 1000ms。
# 生产环境不要设置太小,否则网络抖动、机器卡顿都可能导致误判。
sentinel down-after-milliseconds redis-master 1000
# 故障转移超时时间。
sentinel failover-timeout redis-master 60000
# 故障转移后,允许几个从节点同时向新主节点同步。
# 设置为 1 可以降低新主节点压力。
sentinel parallel-syncs redis-master 1
sentinel2.conf 和 sentinel3.conf 内容也一样。
为什么要创建三份配置文件?
因为 Sentinel 运行过程中会自动 rewrite 配置文件。如果三个 Sentinel 共用同一个配置文件,就可能出现并发修改混乱的问题。
4.6 Sentinel 配置项说明
sentinel monitor
bash
sentinel monitor <master-name> <master-ip> <master-port> <quorum>
例如:
bash
sentinel monitor redis-master 172.22.0.2 6379 2
含义:
| 参数 | 说明 |
|---|---|
redis-master |
Sentinel 内部给主节点起的名字 |
172.22.0.2 |
主节点 IP |
6379 |
主节点端口 |
2 |
quorum,判断客观下线需要的法定票数 |
注意:quorum 只用于判断主节点是否客观下线,不等于真正执行故障转移需要的全部条件。
真正执行故障转移时,Sentinel Leader 还需要获得 Sentinel 多数派授权。
例如:
text
5 个 Sentinel
quorum = 2
表示:
text
至少 2 个 Sentinel 认为主节点下线,可以判定 ODOWN
但是执行故障转移时,Leader 仍然需要获得多数派,也就是至少 3 个 Sentinel 的授权。
所以:
text
quorum 负责判断主节点是否客观下线
majority 负责授权某个 Sentinel 执行故障转移
这两个概念不要混在一起。
sentinel down-after-milliseconds
bash
sentinel down-after-milliseconds redis-master 1000
表示 Sentinel 在指定时间内没有收到有效回复,就认为节点主观下线。
本地测试为了快速看到效果,可以设置成 1000ms。
生产环境不要设置太小,否则容易因为网络抖动产生误判。
sentinel failover-timeout
bash
sentinel failover-timeout redis-master 60000
表示故障转移相关的超时时间。
它会影响:
- 一次 failover 最长持续时间;
- failover 失败后下一次重试的等待;
- Sentinel 对故障转移状态的清理。
sentinel parallel-syncs
bash
sentinel parallel-syncs redis-master 1
表示故障转移后,允许多少个从节点同时向新主节点发起同步。
如果设置过大,多个从节点同时全量复制,可能给新主节点造成较大压力。
所以一般可以设置为 1,让从节点一个一个同步。
4.7 启动 Sentinel 节点
创建 Sentinel 的 docker-compose.yml:
yaml
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:
- 26379: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:
- 26380: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:
- 26381:26379
networks:
default:
external:
name: redis-sentinel-net
启动:
bash
docker-compose up -d
查看日志:
bash
docker-compose logs
Sentinel 启动后,会连接主节点,并通过主节点发现它的从节点。
4.8 观察 Sentinel 配置自动 rewrite
Sentinel 运行后,会自动修改自己的配置文件。
再次打开 sentinel1.conf,可能看到类似内容:
bash
bind 0.0.0.0
port 26379
sentinel myid 4d2d562860b4cdd478e56494a01e5c787246b6aa
sentinel deny-scripts-reconfig yes
# Generated by CONFIG REWRITE
dir "/data"
sentinel monitor redis-master 172.22.0.2 6379 2
sentinel down-after-milliseconds redis-master 1000
sentinel failover-timeout redis-master 60000
sentinel parallel-syncs redis-master 1
sentinel config-epoch redis-master 0
sentinel leader-epoch redis-master 0
sentinel known-replica redis-master 172.22.0.3 6379
sentinel known-replica redis-master 172.22.0.4 6379
sentinel known-sentinel redis-master 172.22.0.5 26379 xxxxxxxxxxxxxxxxxxxx
sentinel known-sentinel redis-master 172.22.0.6 26379 yyyyyyyyyyyyyyyyyyyy
sentinel current-epoch 0
其中:
bash
# Generated by CONFIG REWRITE
后面的内容就是 Sentinel 自动生成和维护的。
可以看到 Sentinel 自动记录了:
- 当前监控的主节点;
- 已发现的从节点;
- 已发现的其他 Sentinel;
- 当前 epoch;
- leader epoch;
- config epoch。
这也是为什么 Sentinel 的配置文件必须可写。
五、Sentinel 常用命令
连接 Sentinel:
bash
redis-cli -p 26379
注意:Sentinel 本身也是 Redis 协议服务,所以可以用 redis-cli 连接。
5.1 查看所有主节点
bash
127.0.0.1:26379> SENTINEL masters
可以查看 Sentinel 当前监控的所有主节点。
5.2 查看指定主节点信息
bash
127.0.0.1:26379> SENTINEL master redis-master
可以查看名为 redis-master 的主节点信息。
5.3 查看从节点信息
Redis 5 中可以使用:
bash
127.0.0.1:26379> SENTINEL slaves redis-master
新版本中也可能看到:
bash
SENTINEL replicas redis-master
本篇使用 Redis 5.0,所以记住 slaves 即可。
5.4 查看其他 Sentinel 节点
bash
127.0.0.1:26379> SENTINEL sentinels redis-master
可以查看当前 Sentinel 已经发现的其他 Sentinel 节点。
5.5 查询当前主节点地址
bash
127.0.0.1:26379> SENTINEL get-master-addr-by-name redis-master
返回类似:
text
1) "172.22.0.2"
2) "6379"
客户端就是通过这个能力获取当前真正的 Redis 主节点地址。
5.6 手动触发故障转移
bash
127.0.0.1:26379> SENTINEL failover redis-master
这个命令可以手动触发一次故障转移。
注意:这不是生产中随便执行的命令,主要用于测试或特殊运维场景。
六、客户端如何接入 Sentinel
很多人容易误解 Sentinel 的"通知"功能,以为 Sentinel 会自动修改应用配置。
实际上不是。
Sentinel 不会直接修改你的 Java 配置文件,也不会把你的应用连接强行切走。
正确做法是:
text
应用客户端支持 Sentinel 模式
↓
应用配置 Sentinel 地址列表和 masterName
↓
客户端向 Sentinel 查询当前 master 地址
↓
客户端连接真正的 Redis master
↓
发生故障转移后,客户端重新向 Sentinel 获取新 master 地址
核心命令就是:
bash
SENTINEL get-master-addr-by-name redis-master
客户端大致流程:
text
应用启动
↓
连接 Sentinel 节点
↓
查询 redis-master 当前对应的主节点地址
↓
连接 Redis 主节点执行读写
↓
主节点故障转移
↓
客户端感知连接异常或收到 Sentinel 事件
↓
重新查询新的主节点地址
↓
连接新的主节点
所以在实际项目中,客户端配置通常不是直接写 Redis 主节点地址,而是写 Sentinel 地址列表和 masterName。
例如 Java 客户端配置思路一般是:
text
sentinel nodes:
192.168.1.10:26379
192.168.1.11:26379
192.168.1.12:26379
master name:
redis-master
这样客户端不需要关心当前主节点到底是哪一台,主节点变化后由 Sentinel 和客户端一起完成发现和切换。
七、故障转移验证
7.1 手动停止主节点
停止 Redis 主节点:
bash
docker stop redis-master
观察 Sentinel 日志:
bash
docker-compose logs
可以看到类似事件。
主观下线
text
+sdown master redis-master 172.22.0.2 6379
表示某个 Sentinel 认为主节点主观下线。
客观下线
text
+odown master redis-master 172.22.0.2 6379 #quorum 3/2
表示有 3 个 Sentinel 认为主节点故障,超过了配置的 quorum=2,于是主节点被判定为客观下线。
开始故障转移
text
+try-failover master redis-master 172.22.0.2 6379
表示 Sentinel 开始尝试故障转移。
Leader 选出
text
+elected-leader master redis-master 172.22.0.2 6379
表示当前 Sentinel 被选为本轮故障转移的 Leader。
新主节点被选出
text
+selected-slave slave 172.22.0.4:6379 172.22.0.4 6379 @ redis-master 172.22.0.2 6379
表示 Leader 选择了某个从节点作为新的主节点候选。
晋升新主节点
text
+promoted-slave slave 172.22.0.4:6379 172.22.0.4 6379 @ redis-master 172.22.0.2 6379
表示该从节点被提升为新的主节点。
故障转移完成
text
+failover-end master redis-master 172.22.0.4 6379
表示故障转移完成。
此时可以连接新主节点查看:
bash
redis-cli -h 172.22.0.4 -p 6379
执行:
bash
INFO replication
可以看到:
text
role:master
7.2 原主节点恢复
重新启动原主节点:
bash
docker start redis-master
此时原主节点不会重新夺回主节点身份,而是会被 Sentinel 改造成新主节点的从节点。
连接原主节点:
bash
redis-cli -p 6379
执行:
bash
127.0.0.1:6379> INFO replication
可以看到类似结果:
text
# Replication
role:slave
master_host:172.22.0.4
master_port:6379
master_link_status:up
slave_repl_offset:324475
slave_read_only:1
说明原来的主节点恢复后,已经变成了从节点,开始复制新的主节点。
7.3 验证当前主节点地址
连接任意 Sentinel:
bash
redis-cli -p 26379
执行:
bash
SENTINEL get-master-addr-by-name redis-master
返回:
text
1) "172.22.0.4"
2) "6379"
说明 Sentinel 已经把当前主节点地址更新为新的主节点。
八、选举原理(核心重点)
当 Redis 主节点发生故障后,Sentinel 的完整处理流程如下:
text
主节点故障
↓
1. Sentinel 发现主节点心跳异常
↓
2. 主观下线 SDown
↓
3. 多个 Sentinel 协商
↓
4. 客观下线 ODown
↓
5. Sentinel 之间选举 Leader
↓
6. Leader 从从节点中选择新主节点
↓
7. 新主节点执行 SLAVEOF NO ONE
↓
8. 其他从节点复制新主节点
↓
9. 原主节点恢复后变成从节点
↓
10. 客户端连接到新的主节点
下面逐步拆开讲。
8.1 主观下线(SDown)
Sentinel 会周期性向 Redis 节点发送心跳检测命令。
如果某个 Sentinel 在 down-after-milliseconds 配置的时间内,一直没有收到主节点的有效回复,它就会认为主节点不可达。
此时,这个 Sentinel 会把主节点标记为:
text
Subjectively Down
也就是主观下线,简称 SDown。
主观下线是单个 Sentinel 的判断。
例如:
text
sentinel1 认为 master 下线
sentinel2 认为 master 正常
sentinel3 认为 master 正常
这时不能马上故障转移。
因为有可能是 sentinel1 自己网络异常,导致它访问不到 master,而不是 master 真的挂了。
所以,主观下线只是第一步。
8.2 客观下线(ODown)
当某个 Sentinel 认为主节点主观下线后,它会询问其他 Sentinel:
text
你们觉得 master 下线了吗?
如果认为主节点下线的 Sentinel 数量达到配置的 quorum,主节点就会被判定为:
text
Objectively Down
也就是客观下线,简称 ODown。
例如配置:
bash
sentinel monitor redis-master 172.22.0.2 6379 2
最后的 2 就是 quorum。
表示至少需要 2 个 Sentinel 认为主节点下线,才会判定主节点客观下线。
text
sentinel1:master 下线
sentinel2:master 下线
sentinel3:master 正常
下线票数 = 2
quorum = 2
所以 master 进入 ODown
客观下线的作用是防止单个 Sentinel 误判。
但是还要注意:
text
ODown 只是确认主节点故障
并不代表任意 Sentinel 都能直接执行故障转移
真正执行故障转移,还需要选举出一个 Sentinel Leader,并且这个 Leader 要获得 Sentinel 多数派授权。
8.3 Leader 选举(类 Raft 的多数派选举)
主节点被判定为客观下线后,Sentinel 节点之间会选举出一个 Leader。
由 Leader 负责后续故障转移。
这里可以把它理解为一种类 Raft 的多数派选举机制。
为什么说是"类 Raft"?
因为 Sentinel 的选举过程有 Raft 的影子,比如:
- 使用 epoch 区分不同轮次;
- 每个 Sentinel 在同一个 epoch 中只能投一票;
- 候选 Sentinel 需要获得多数派授权;
- 同一轮故障转移只允许一个 Leader 执行。
但是它不是完整的 Raft。完整 Raft 还包括日志复制、日志一致性、状态机复制等内容。Sentinel 这里主要用的是"多数派投票选 Leader"的思想。
可以这样理解。
假设有三个 Sentinel:
text
S1
S2
S3
主节点客观下线后,三个 Sentinel 都可能尝试成为 Leader。
第一步:发起拉票
每个 Sentinel 都可能向其他 Sentinel 发送拉票请求。
text
S1 → S2、S3
S2 → S1、S3
S3 → S1、S2
第二步:收到请求后投票
每个 Sentinel 在同一个 epoch 里只有一票。
如果它还没投过票,就可以投给第一个向它拉票的 Sentinel。
如果已经投过票,就不能再投给别人。
text
S2 第一个收到 S1 的请求
↓
S2 把票投给 S1
后面 S3 再来找 S2 拉票
↓
S2 已经投过票
↓
拒绝
第三步:获得多数票成为 Leader
如果某个 Sentinel 获得超过半数票,就成为本轮故障转移 Leader。
三个 Sentinel 的多数派是 2。
所以只要某个 Sentinel 拿到 2 票,就可以成为 Leader。
text
S1 获得 S1 自己的一票 + S2 的一票
↓
共 2 票
↓
超过半数
↓
S1 成为 Leader
如果没有 Sentinel 获得多数票,就进入下一轮 epoch,重新投票。
8.4 为什么建议 Sentinel 节点是奇数个
生产环境中,Sentinel 通常建议至少 3 个,并尽量部署在不同机器上。
常见数量是:
text
3 个 Sentinel
5 个 Sentinel
为什么一般建议奇数个?
因为 Sentinel 执行故障转移需要多数派授权。奇数个节点在容错能力和资源成本之间更划算。
例如:
text
3 个 Sentinel,多数派是 2,最多容忍 1 个 Sentinel 不可用。
4 个 Sentinel,多数派是 3,最多也只能容忍 1 个 Sentinel 不可用。
4 个 Sentinel 比 3 个多部署了一个节点,但是容错能力没有提升。
所以实际使用中,3 个或 5 个更常见。
8.5 Leader 如何选择新的主节点
Leader 选出来后,就要从剩余的从节点中选择一个作为新的主节点。
不过 Leader 不是直接在所有从节点里排序,而是先过滤掉不适合晋升的从节点。
第一步:过滤不合格从节点
以下从节点通常不会被选为新主节点:
- 已经主观下线或不可达的从节点;
- 与原主节点断开太久、数据明显落后的从节点;
slave-priority或replica-priority配置为 0 的从节点。
其中:
bash
slave-priority 0
表示这个从节点永远不要被 Sentinel 提升为主节点。
Redis 新版本中也可以看到:
bash
replica-priority 0
Redis 5 中通常还是 slave-priority 这个配置名。
第二步:按优先级排序
剩下的候选从节点,先比较优先级。
配置项:
bash
slave-priority 100
数值越小,优先级越高。
例如:
text
slave1 priority = 100
slave2 priority = 50
那么 slave2 优先被选为新主节点。
如果某台从节点机器配置更好、网络更稳定、机房位置更合适,就可以人为把它的优先级调高。
注意:优先级高,指的是数值更小。
第三步:比较复制偏移量 offset
如果优先级相同,就比较复制偏移量。
offset 越大,说明这个从节点复制到的数据越新,越接近原主节点。
例如:
text
slave1 offset = 10000
slave2 offset = 12000
说明 slave2 同步的数据更多,因此 slave2 更适合作为新主节点。
这个规则可以尽量减少故障切换时的数据丢失。
可以用上一篇主从复制里的例子理解:
老师一共有 100 页课件。
text
助教 A 学到了第 80 页
助教 B 学到了第 95 页
如果老师突然请假,肯定优先让学到第 95 页的助教来代课。
第四步:比较 run ID
如果优先级一样,offset 也一样,就比较 run ID。
run ID 是 Redis 实例本次运行的唯一标识,可以通过 INFO server 查看:
bash
INFO server
里面会有:
text
run_id:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
这里比较的是 run ID 的字典序,字典序更小的节点优先。
注意:这里使用的是 run_id,不是上一篇主从复制中 PSYNC 使用的 replid。
这两个概念不要混淆:
text
replid:复制历史 ID,用于 PSYNC 判断是否能部分复制
run_id:Redis 进程本次运行 ID,Sentinel 选新主节点时可作为最后兜底排序条件
8.6 Leader 执行故障转移
选出新的主节点后,Leader 会执行以下操作。
第一步:让目标从节点晋升为主节点
Leader 向被选中的从节点发送:
bash
SLAVEOF NO ONE
这个从节点断开与旧主节点的复制关系,晋升为新的主节点。
新版本也可以理解为:
bash
REPLICAOF NO ONE
不过 Redis 5 中常见命令仍然是 SLAVEOF。
第二步:让其他从节点复制新主节点
Leader 向其他从节点发送:
bash
SLAVEOF <newMasterIp> <newMasterPort>
让它们复制新的主节点。
第三步:更新 Sentinel 内部配置
Sentinel 会更新自己记录的主节点地址,并通过配置 rewrite 写入配置文件。
其他 Sentinel 也会感知到新的主节点配置。
第四步:原主节点恢复后改造成从节点
如果原来的主节点恢复了,Sentinel 不会让它重新抢回主节点位置,而是让它执行:
bash
SLAVEOF <newMasterIp> <newMasterPort>
让它作为从节点加入当前主从结构。
九、Sentinel 的局限性
Sentinel 解决的是高可用问题,但它不是万能的。
9.1 Sentinel 不保证零数据丢失
Redis 主从复制是异步复制。
主节点写入成功后,数据可能还没来得及同步给从节点。
如果此时主节点宕机,Sentinel 把某个从节点提升为新主节点,那么刚才那部分还没同步过去的数据就可能丢失。
例如:
text
客户端写入 key=hello
↓
主节点返回成功
↓
数据还没同步给从节点
↓
主节点宕机
↓
从节点被提升为新主节点
↓
key=hello 丢失
所以:
text
Sentinel 解决自动故障转移
不保证强一致
不保证故障时零数据丢失
可以通过下面配置降低风险:
bash
min-slaves-to-write 1
min-slaves-max-lag 10
新版本配置名是:
bash
min-replicas-to-write 1
min-replicas-max-lag 10
意思是:至少有 1 个从节点延迟不超过 10 秒,主节点才接受写入。
但这只能降低风险,不能把 Redis Sentinel 变成强一致系统。
9.2 Sentinel 不能解决存储容量问题
Sentinel 架构下,主节点和从节点仍然保存完整数据。
如果数据量接近或超过单机内存容量,Sentinel 解决不了这个问题。
例如:
text
主节点 100GB 数据
从节点也要 100GB 数据
它没有把数据拆分到多台机器上。
如果要解决存储容量问题,需要 Redis Cluster,也就是集群分片。
9.3 Sentinel 对客户端有要求
应用不是直接写死 Redis 主节点地址,而是要使用支持 Sentinel 的客户端。
如果客户端不支持 Sentinel,它就无法自动感知主节点变化。
所以 Sentinel 高可用需要两部分配合:
text
服务端:Sentinel 自动完成故障转移
客户端:支持 Sentinel 模式,自动发现新主节点
9.4 Docker / NAT 环境要注意地址问题
本篇 Docker 配置主要用于学习。
在生产环境中,如果 Redis、Sentinel、客户端处于 Docker、NAT、端口映射环境中,要特别注意地址可达性。
Sentinel 发现的地址,必须是客户端也能访问的地址。
否则可能出现:
text
Sentinel 已经完成故障转移
客户端也查到了新主节点
但是客户端拿到的是容器内部 IP
导致客户端无法连接
生产中需要结合实际网络环境配置:
- 固定 IP;
- host 网络;
- announce-ip;
- announce-port;
- 客户端可达地址;
- 避免错误的端口映射。
十、总结
现在你已经掌握了 Redis Sentinel 的核心内容。
10.1 Sentinel 解决的问题
Redis Sentinel 解决的是主从复制下主节点故障后需要人工介入的问题。
它可以自动完成:
- 故障发现;
- 故障确认;
- Leader 选举;
- 从节点晋升;
- 主从关系重建;
- 客户端主节点发现。
10.2 Sentinel 的三个核心功能
| 功能 | 说明 |
|---|---|
| 监控 | 定期检测主节点、从节点、其他 Sentinel 是否可达 |
| 故障转移 | 主节点故障后,自动选择一个从节点晋升为新主节点 |
| 通知 | 客户端通过 Sentinel 获取当前主节点地址,故障后重新连接新主节点 |
10.3 主观下线和客观下线
| 概念 | 含义 |
|---|---|
| 主观下线 SDown | 单个 Sentinel 认为某个节点不可达 |
| 客观下线 ODown | 多个 Sentinel 达成共识,认为主节点确实不可达 |
主观下线是单点判断,可能误判。
客观下线需要达到 quorum,可信度更高。
10.4 quorum 和 majority 的区别
这两个概念非常容易混淆。
text
quorum:判断主节点是否客观下线需要的票数
majority:授权某个 Sentinel 执行故障转移需要的多数派
例如:
text
5 个 Sentinel
quorum = 2
表示:
text
2 个 Sentinel 认为 master 下线,即可判定 ODown
但是执行 failover 时,仍然需要至少 3 个 Sentinel 授权。
10.5 Leader 选举
Sentinel Leader 选举可以理解为一种类 Raft 的多数派选举机制:
- 每个 epoch 是一轮选举;
- 每个 Sentinel 在同一轮只能投一票;
- 获得多数票的 Sentinel 成为 Leader;
- 只有 Leader 负责执行故障转移。
10.6 新主节点选择规则
Leader 选择新主节点时,先过滤不合格从节点,然后排序。
过滤条件包括:
- 从节点不可达;
- 从节点数据太旧;
slave-priority为 0。
排序规则:
text
slave-priority 数值越小越优先
↓
复制 offset 越大越优先
↓
run ID 字典序越小越优先
10.7 Sentinel 的注意事项
- Sentinel 节点不能只有一个,否则 Sentinel 自己也会成为单点。
- Sentinel 建议至少 3 个,并尽量部署在不同机器上。
- Sentinel 节点不存储业务数据,数据仍然存储在 Redis 主从节点中。
- 客户端必须支持 Sentinel 模式,才能自动感知主节点变化。
- Sentinel 解决的是高可用问题,不解决存储容量问题。
- Sentinel 基于异步复制,不能保证极端情况下零数据丢失。
- Docker / NAT 环境中要特别注意 Sentinel 返回地址是否对客户端可达。
下一篇预告:Redis 集群(Cluster)------ 如何突破单机存储限制?数据分片、哈希槽、集群故障转移机制。