【Redis篇】Redis 哨兵(Sentinel):高可用自动故障转移

文章目录

    • [Redis 哨兵(Sentinel):高可用自动故障转移](#Redis 哨兵(Sentinel):高可用自动故障转移)
    • 一、前言
    • 二、主从复制的遗留问题
    • 三、哨兵是什么
      • [3.1 基本概念](#3.1 基本概念)
      • [3.2 哨兵的整体架构](#3.2 哨兵的整体架构)
      • [3.3 哨兵的三个核心功能](#3.3 哨兵的三个核心功能)
    • [四、安装部署(基于 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)
    • 七、故障转移验证
    • 八、选举原理(核心重点)
    • [九、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-hostnamesannounce-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.confsentinel3.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-priorityreplica-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)------ 如何突破单机存储限制?数据分片、哈希槽、集群故障转移机制。


相关推荐
qfljg1 小时前
oracle 迁移到postgres
数据库·oracle
giaz14n9X2 小时前
Redis 分布式锁进阶第五十七篇
数据库·redis·分布式
剑神一笑2 小时前
Linux ls 命令深度解析:从目录遍历到颜色输出的实现原理
linux·服务器·数据库
Maynor9962 小时前
Codex API 网关迁移与流量优化实战
数据库·oracle
WyCAGy8ij3 小时前
Redis 分布式锁进阶第二篇讲解
数据库·redis·分布式
南极企鹅3 小时前
MySQL的两大支柱:undo Log&redo log
数据库·mysql·oracle
智航GIS3 小时前
ArcGIS大师之路500技---078文件数据库的加密与解密
数据库·arcgis
音乐宝贝家3 小时前
吉他面板材质怎么选?云杉单板面单吉他配置深度解析
数据库·新媒体运营·产品运营·媒体·材质·内容运营
2401_873479404 小时前
企业安全运营中,如何用IP离线库提前发现失陷主机?三步实现风险画像
网络·数据库·python·tcp/ip·ip