前阵子把一个项目的 Redis 从单机改成哨兵模式,整个过程踩了不少坑,也把之前只停留在理论层面的自动故障转移和读写分离真正跑了一遍。这篇把我实际设计思路、配置细节和踩坑记录整理出来,给正在做同样改造的人省点时间。
先交代一下背景:线上有一台 Redis 单点,平时看着挺稳,结果有一天凌晨它挂了,上游的请求瞬间后撤到数据库,重试风暴直接把数据库也压垮了。那次事故之后我的目标就很明确:Redis 不能单点裸奔,至少要保证主节点挂了能自动切换;顺便把读流量从主节点上分担出去,降低主节点压力。
这篇文章会从环境搭建讲起,到 Spring Boot 配置、读写分离实现、故障转移实测,再到常见问题的排查思路,全程都是可以照着复现的操作。适合正在维护小型到中型 Redis 部署、想把高可用和读写分离落地的 Java 后端同学,也适合准备面试时想真正理解哨兵机制的人。
1. 为什么必须折腾哨兵模式
1.1 单节点 Redis 的痛点与事故复盘
先说说那次事故。凌晨的流量其实不高,但那个 Redis 节点承载的不只是缓存,还有一个延时队列和一个库存预扣的计数。节点一挂,所有依赖 Redis 的接口基本全部超时,服务端的重试逻辑开始疯狂重发,数据库连接池瞬间被打满,最终整个应用不可用。事后复盘,最根本的原因就两个:没有副本、没有自动切换机制。
很多团队对 Redis 的定位是“缓存而已”,挂了重新加载就行。但 Redis 一旦承担了分布式锁、计数、队列这些有状态的功能,单节点就变成了系统里最脆弱的环节。所以我个人的建议是,只要项目里有用到 Redis 做“非纯缓存”的事情,就应该认真考虑主从加哨兵的架构,这比临时扩容一台机器靠谱得多。
1.2 主从复制和哨兵模式的区别
主从复制解决的是读扩展和容灾备份的问题:主节点负责写,从节点复制主节点的数据,读请求可以打到从节点。但主从复制有一个致命缺陷:主节点挂了,系统不会自动把从节点提升为主节点,需要人工介入。
哨兵模式就是在主从复制之上增加了监控、通知和自动故障转移三个能力。用一个不太严谨但好理解的比喻:哨兵就像小区保安,不仅实时盯着大门是否正常,还和隔壁几个保安互相通信;主大门一坏,立刻把备用门启用,住户几乎无感。
哨兵自身的三个核心机制值得展开一下:
- 监控:每个哨兵每秒向主从节点发送 PING,超过 down-after-milliseconds 配置的时间没响应,就标记为主观下线。
- 通信与决议:哨兵之间通过 Redis 的发布订阅机制交换信息,当标记主节点主观下线的哨兵数量达到 quorum 配置的值,就升级为主观下线判定后的客观下线。
- 故障转移:由选举出的 leader 哨兵挑选一个从节点提升为新主节点,并让其他从节点去复制新主节点。
这三个机制决定了哨兵模式对外表现出的“自动”和“高可用”,后面的故障转移实测部分我会展示完整的日志链路。
1.3 为什么不直接用 Redis Cluster
有人可能会问:既然要高可用,为什么不直接上 Redis Cluster。我在这个项目里没选 Cluster,主要是这几个原因:第一,项目写扩展需求不高,主节点写入量远没到单机瓶颈;第二,Cluster 限制了多键操作,比如跨 slot 的事务、Lua 脚本、MGET 都需要在同一个哈希槽里才能保证原子性,老代码改造量不小;第三,哨兵模式对已有代码几乎无感,只需要改 spring 配置和少量 Bean,迁移成本低。
哨兵模式适合的数据量级是单机内存能扛住的场景,比如几十 GB 以内的缓存、会话、计数类数据。如果数据量已经多到单机装不下,或者写并发高到单点 CPU 扛不住,那确实应该考虑 Cluster。但如果你只是想要高可用,哨兵模式是性价比最高的选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:用 Docker Compose 一分钟拉起主从加哨兵
2.1 目录规划与配置文件清单
为了快速复现,我在本机和测试环境都用 Docker Compose 来部署,整体结构是:1 个主节点、2 个从节点、3 个哨兵节点。这个组件的数量是经过考虑的:哨兵要满足 quorum 和 majority,最少也要 3 个节点才能在一个节点挂掉后还能完成选举;从节点建议至少 2 个,既承担读流量,也在主节点故障后有余量。
目录结构如下,配置全部放在 config 目录,方便统一管理和修改:
bash复制redis-sentinel-demo/
├── docker-compose.yml
├── config/
│ ├── master.conf
│ ├── replica1.conf
│ ├── replica2.conf
│ ├── sentinel1.conf
│ ├── sentinel2.conf
│ └── sentinel3.conf
2.2 主从节点 Redis 配置
主节点的配置很简单,我用的 Redis 7.0,开启 AOF 保证持久化:
conf复制# master.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
appendfsync everysec
两个从节点配置基本相同,唯一的关键是增加 replicaof 指向主节点:
conf复制# replica1.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
appendfsync everysec
replicaof redis-master 6379
replica-read-only yes
replica-read-only 这个参数默认值是 yes,也就是从节点默认只读,这个必须保持开启,否则可能出现从节点被误写入数据,造成主从数据不一致。从节点追主节点的过程,推荐配合 AOF 一起使用,虽然全量同步依赖的是 RDB,但 AOF 能在节点意外重启后快速恢复本地数据,减少主从需要重新全量同步的概率。
2.3 哨兵节点配置与关键参数含义
三个哨兵节点配置内容基本一样,只有端口和日志输出的容器名不同:
conf复制# sentinel1.conf
port 26379
bind 0.0.0.0
protected-mode no
sentinel monitor mymaster redis-master 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
这里几个参数逐个解释一下:
sentinel monitor mymaster redis-master 6379 2:监控名为 mymaster 的 Redis 主从组,主节点地址是 docker-compose 里的服务名 redis-master,端口 637
