如果你手头有一批 Redis 实例要管,又暂时不想上 Kubernetes 那套重家伙,哨兵模式几乎是高可用最经典、最省心的方案。不过在容器环境里把哨兵搭起来,很多人在配置文件上栽过跟头——原生 Redis 镜像的哨兵配置要手写sentinel.conf,一个缩进、一个换行不对,整个集群都不给你好脸色看。我最近半年一直在用bitnami/redis-sentinel这套镜像,把 Redis 哨兵模式从 Docker Compose 部署、故障转移演练到 Spring Boot 应用接入完整蹚了一遍,今天把整个过程和踩过的坑都整理出来。想直接抄作业的,照着做就行。
这篇内容适合三类人:一是刚开始接触 Redis 高可用、想在本地快速搭一套哨兵环境练手的学习者;二是团队里负责中间件运维、想在测试环境验证故障转移流程的工程师;三是正打算用 Docker Compose 管理一批 Redis 实例、又不想写一堆原生配置的开发者。我会先从哨兵模式的核心机制讲起,再解释为什么选 Bitnami 这套镜像,最后给你一套可以直接复制的完整部署和验证流程。
1. 先弄懂哨兵模式在解决什么问题
1.1 主从复制只是高可用的第一步
很多人一开始接触 Redis 主从复制时,会觉得“我主节点写、从节点读,数据有两份了,这不就高可用了吗”。但从实际运行的角度看,主从复制只解决了“数据冗余”和“读写分离”的问题,它没有解决“可用性”的问题。
我可以打个比方:主从复制就是公司里一个领导和几个下属的关系,领导每天把工作记录同步给下属,下属只负责执行同类任务。可如果领导突然住院了,公司并不会自动推举一个新的领导出来,所有需要领导审批的事情全部卡住。对系统来说,就是主节点宕机了,写请求全部失败,虽然从节点上还有一份数据,但没有任何机制自动把从节点提升为新的主节点,也没有任何机制通知客户端“你该去连新主库了”。
那手动处理行不行?行,但在生产环境里,人工介入的延迟通常意味着业务损失。凌晨三点主节点宕机,等值班工程师起床、看日志、执行SLAVEOF切换,可能已经过去一两个小时。而且人工操作 Redis 主从切换很容易出错,尤其是当你面对的是几十个节点的时候。所以我们需要一个自动化的监督者,这就是哨兵(Sentinel)诞生的理由。
1.2 哨兵到底“哨”什么:监控、通知、自动故障转移
Redis 哨兵模式不是一个独立的数据存储组件,它是一组运行在特殊模式下的 Redis 进程,官方叫redis-sentinel或redis-server --sentinel。它干的事情,简单说就是三件:
- 监控:哨兵会周期性地向所有主节点、从节点以及其他哨兵节点发送 PING 命令,确认它们是否还活着。
- 通知:当某个节点出现异常时,哨兵可以通过 Pub/Sub 机制通知管理员或者其他应用程序。
- 自动故障转移:当主节点被认为不可用时,哨兵会在从节点中选举一个提升为新的主节点,并通知其他从节点改从新主节点复制,同时通知客户端新的主节点地址。
这里要特别强调一个很多人搞混的点:哨兵不是 Redis 数据的代理或网关。客户端并不是通过 “连上哨兵然后把操作命令发过去” 来读写数据,而是先向哨兵询问“当前主节点在哪”,然后直接去连接真正的主节点。也就是说,哨兵只是扮演了一个“注册中心”或者“服务发现”的角色,真正的数据读写还是客户端和 Redis 节点之间直连。
在机制上,哨兵判断主节点不可用分两步。第一步叫主观下线(Subjectively Down,简称 S_DOWN),单个哨兵在指定时间内没有收到主节点的有效响应,就主观认为它下线了。但这可能是网络抖动,或者哨兵自己出了问题,所以不能立刻做故障转移。第二步叫客观下线(Objectively Down,简称 O_DOWN),当多个哨兵(数量达到配置的 quorum 值)都认为主节点不可用时,才会真正进入故障转移流程。
故障转移的核心动作是从候选从节点里挑一个“新主”。挑选的依据是优先级、复制偏移量、运行 ID 等。选出来后,哨兵会向这个从节点发送SLAVEOF NO ONE命令,让它成为新主节点,然后让其他从节点改为复制新主。整个过程中,客户端需要能感知主节点变化,所以生产环境中客户端通常要配置哨兵地址列表,通过哨兵动态获取当前主节点。这也是后面我们接入 Spring Boot 时要做的事。
2. 为什么我推荐 bitnami/redis-sentinel 这套镜像
2.1 原生镜像搭哨兵的痛点
如果你拿官方redis镜像直接搭哨兵,会发现在容器环境下有几个很麻烦的地方。
首先是配置文件。原生哨兵模式要求你准备一份sentinel.conf,里面至少要有sentinel monitor <master-name> <ip> <port> <quorum>这种配置行。在实际使用中,你还需要指定sentinel down-after-milliseconds(判断主观下线的超时时间)、sentinel failover-timeout(故障转移总超时)、sentinel parallel-syncs(故障转移后同时同步新主的从节点数量)这些参数。写一份生产可用的配置不算难,但要把它和镜像打包、挂载、在不同环境里维护,麻烦事就多了。
第二个痛点是 IP 和端口的动态性问题。Docker 容器每次启动后 IP 可能变化,如果用固定 IP,跨主机部署又不方便。原生sentinel.conf里的sentinel monitor写的是某个 IP,一旦容器重建、IP 变了,配置就失效了,你得手工改文件再重启。这在测试环境折腾几轮下来,很容易让人崩溃。
第三个痛点是主从关系变化后配置文件会被哨兵自动改写。Redis 哨兵在故障转移完成后,会重写自己的配置文件来记录新主节点状态。但在官方镜像里,配置文件默认放在只读层或没有持久化到宿主机,容器一重建,这些记录就丢失了,又得从头手动初始化一遍。
2.2 bitnami 镜像的配置逻辑和优势
Bitnami 这套镜像的思路和原生镜像很不一样。它把配置逻辑从“维护配置文件”变成“声明环境变量”,你只需要在 Docker Compose 里通过环境变量告诉镜像“我是主节点”“我是从节点”“我是哨兵,我盯着谁”,剩下的初始化逻辑、配置文件生成、主从关系注册都由镜像内部的脚本自动完成。
举个例子,原生 Redis 镜像中,如果要用密码认证,你需要在配置里写requirepass,还要确保从节点配置masterauth,哨兵配置访问主节点密码。这一套联动很容易漏。Bitnami 的处理方式是通过REDIS_PASSWORD、REDIS_MASTER_PASSWORD这些环境变量来传递认证信息,镜像脚本会在所有节点上自动写入对应的配置项。
另外,Bitnami 的redis-sentinel镜像和redis镜像是配套设计的。它们在同一个 Docker 网络里通过容器名互相解析,哨兵镜像通过REDIS_MASTER_HOST自动获知主节点的地址。配合 Compose 里的depends_on和健康检查,基本能做到启动即集群,不再需要手工折腾配置文件。
有一点我需要事先说明:Bitnami 镜像内部是有不少“自有逻辑”的,所以它的目录结构、环境变量、命令行为跟原生镜像不完全一样。你要维护它,最好以 Bitnami 官方文档为准,不要理所当然地认为自己熟悉原生 Redis 就能直接猜出路径。
这套镜像还有一个很实用的点:它把 Redis 主节点、从节点、哨兵拆成两种镜像,实际上redis镜像用REDIS_REPLICATION_MODE环境变量区分主从,redis-sentinel镜像是独立的。部署时,主节点和从节点用bitnami/redis,哨兵节点用bitnami/redis-sentinel,分工清晰,配置参数也好理解。
3. 部署前的规划:端口、目录、版本一个都不能少
3.1 版本选型与镜像搭配
部署任何一个中间件集群,我都建议先把版本确定下来,而不是随手latest一把梭。Bitnami 的镜像版本标签说得比较明确,比如bitnami/redis:7.0和bitnami/redis-sentinel:7.0。这里有一个必须注意的原则:Redis Servers 和 Sentinels 的版本要一致。如果你主从是 Redis 7.0,哨兵却是 6.2,虽然大部分情况下能工作,但在某些命令行为、协议细节上会有差异,跨版本容易出现排查不清的诡异问题。
我这次用的是 Redis 7.0 系列。选它的原因很简单:7.x 是当前生产环境的主流版本,自身引入了不少性能优化,而且 Bitnami 镜像对 7.0 的支持已经非常成熟。如果你所在公司用的是 6.2 或者更老的版本,也不用慌,部署逻辑完全一样,只要把镜像标签改成对应版本号即可。
用到的镜像一共两个,责任划分是这样的:
bitnami/redis:启动 Redis Server,通过REDIS_REPLICATION_MODE=master或replica来指定是主节点还是从节点。bitnami/redis-sentinel:启动 Sentinel 进程,通过REDIS_MASTER_HOST指定要监控的主节点,不直接参与 Redis 数据读写。
3.2 网络与目录规划
我建议在 Docker 里创建独立的自定义网络,而不是用默认 bridge。原因很朴素:自定义网络自带 DNS 解析,容器之间可以直接用服务名互相访问,这样哨兵配置里的REDIS_MASTER_HOST可以直接填 Compose 里的服务名(比如redis-master),不用去查容器 IP。如果你用默认 bridge,除非手动--link,不然容器名解析是不稳定的,在 Compose 里也会产生一堆额外配置。
端口方面,Redis Server 默认监听 6379,Redis Sentinel 默认监听 26379。生产环境通常不会把这两个端口直接暴露到公网,所以我只在需要外部访问的节点上映射端口。举个例子,如果你只是本地验证,可以在主节点上把 6379 映射到宿主机 16379;如果想让应用容器在同一个 Docker 网络内访问,其实完全不需要映射端口,直接用服务名加 6379 就能连通。哨兵的 26379 端口,对外主要给客户端获取主节点地址用,本地验证时映射到宿主机 26379 即可。
数据持久化也要提前规划。Bitnami 镜像默认会把数据写在/bitnami/redis/data目录。我们通过 volume 把主节点和从节点的数据目录挂载到宿主机,防止容器重建后数据丢失。哨兵节点本身不存业务数据,理论上可以不挂持久化目录,但我习惯也挂一份,方便查看它运行时生成的配置文件和日志。记住,持久化目录要预先建好并给足权限,否则容器启动时可能因为权限不足直接退出。
4. 用 Docker Compose 一键拉起整套哨兵集群
4.1 完整 Compose 配置(可直接复制)
先展示一套比较实用的docker-compose.yml。这个配置适合在单机 Docker 环境里模拟“一主两从三哨兵”的拓扑。所谓一主两从,就是一个主节点、两个从节点,读写能力和容错能力都不错;三哨兵是推荐的最小数量,这样在 quorum 设置为 2 时,即使一个哨兵挂了,剩下的两个仍能就主节点状态达成一致,不至于出现脑裂或无法决策的情况。
version: '3.8' networks: redis-sentinel-net: driver: bridge services: redis-master: image: bitnami/redis:7.0 container_name: redis-master environment: - REDIS_REPLICATION_MODE=master - REDIS_PASSWORD=redis123 - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL ports: - "16379:6379" volumes: - ./redis-master-data:/bitnami/redis/data networks: - redis-sentinel-net healthcheck: test: ["CMD", "redis-cli", "-a", "redis123", "ping"] interval: 5s timeout: 3s retries: 3 redis-replica-1: image: bitnami/redis:7.0 container_name: redis-replica-1 environment: - REDIS_REPLICATION_MODE=replica - REDIS_MASTER_HOST=redis-master - REDIS_MASTER_PORT_NUMBER=6379 - REDIS_MASTER_PASSWORD=redis123 - REDIS_PASSWORD=redis123 volumes: - ./redis-replica-1-data:/bitnami/redis/data networks: - redis-sentinel-net depends_on: - redis-master redis-replica-2: image: bitnami/redis:7.0 container_name: redis-replica-2 environment: - REDIS_REPLICATION_MODE=replica - REDIS_MASTER_HOST=redis-master - REDIS_MASTER_PORT_NUMBER=6379 - REDIS_MASTER_PASSWORD=redis123 - REDIS_PASSWORD=redis123 volumes: - ./redis-replica-2-data:/bitnami/redis/data networks: - redis-sentinel-net depends_on: - redis-master sentinel-1: image: bitnami/redis-sentinel:7.0 container_name: sentinel-1 environment: - REDIS_MASTER_HOST=redis-master - REDIS_MASTER_PORT_NUMBER=6379 - REDIS_MASTER_SET_NAME=mymaster - REDIS_SENTINEL_QUORUM=2 - REDIS_MASTER_PASSWORD=redis123 ports: - "26379:26379" volumes: - ./sentinel-1-data:/bitnami/redis-sentinel/data networks: - redis-sentinel-net depends_on: - redis-master sentinel-2: image: bitnami/redis-sentinel:7.0 container_name: sentinel-2 environment: - REDIS_MASTER_HOST=redis-master - REDIS_MASTER_PORT_NUMBER=6379 - REDIS_MASTER_SET_NAME=mymaster - REDIS_SENTINEL_QUORUM=2 - REDIS_MASTER_PASSWORD=redis123 volumes: - ./sentinel-2-data:/bitnami/redis-sentinel/data networks: - redis-sentinel-net depends_on: - redis-master sentinel-3: image: bitnami/redis-sentinel:7.0 container_name: sentinel-3 environment: - REDIS_MASTER_HOST=redis-master - REDIS_MASTER_PORT_NUMBER=6379 - REDIS_MASTER_SET_NAME=mymaster - REDIS_SENTINEL_QUORUM=2 - REDIS_MASTER_PASSWORD=redis123 volumes: - ./sentinel-3-data:/bitnami/redis-sentinel/data networks: - redis-sentinel-net depends_on: - redis-master4.2 启动与自检
把上面的内容保存为docker-compose.yml,然后在你准备存放这个集群的目录里执行:
docker compose up -d第一次启动会拉取镜像,可能要等一会儿。启动后先用docker compose ps看所有容器的状态,正常情况下六个容器都是Up状态,STATUS列没有异常退出或重启。
接下来要检查两个层面:Redis 主从是否建立、哨兵是否认识所有节点。
先看主从复制状态。进入主节点容器:
docker exec -it redis-master redis-cli -a redis123 info replication如果一切正常,你会看到类似这样的关键信息:
# Replication role:master connected_slaves:2 slave0:ip=172.19.0.3,port=6379,state=online,offset=... slave1:ip=172.19.0.2,port=6379,state=online,offset=...connected_slaves:2表示两个从节点都已经连上来并处于在线状态。在测试环境里,这时候你往主节点写一个 key,看两个从节点是否同步,就能初步验证主从复制正常。
再检查 Sentinel 对集群的感知。进入任意哨兵容器,用redis-cli连接本地哨兵端口:
docker exec -it sentinel-1 redis-cli -p 26379 -a redis123注意:这里是否要加-a密码,取决于你部署时有没有设哨兵密码。在我们这个配置里,REDIS_MASTER_PASSWORD是 Redis 主从之间的认证密码,哨兵访问 Redis 时要用它;但 Sentinel 自身如果没有专门配置密码,本地redis-cli连接时通常不需要认证。如果你遇到NOAUTH Authentication required,就加上对应密码参数。
在哨兵命令行里执行:
SENTINEL get-master-addr-by-name mymaster看到返回:
1) "172.19.0.x" 2) "6379"这就说明哨兵已经知道当前主节点的 IP 和端口。再执行:
SENTINEL sentinels mymaster SENTINEL slaves mymaster可以看到哨兵节点列表和从节点列表。这证明集群拓扑信息已经在哨兵之间同步了。
到这里,一个最小的 Redis 哨兵集群就算部署完成。但部署完成只是开始,接下来的故障转移演练才是真正检验配置是否真的靠谱的环节。
5. 故障转移演练:把主节点“打断”看看会发生什么
5.1 验证主从复制的最终效果
在模拟故障之前,我建议先做一些常规写入,确保数据能正常同步。在主节点上写入一个带业务含义的 key:
docker exec -it redis-master redis-cli -a redis123 set order:1001 paid然后去两个从节点上分别查:
docker exec -it redis-replica-1 redis-cli -a redis123 get order:1001 docker exec -it redis-replica-2 redis-cli -a redis123 get order:1001正常情况下都能查到这个值。如果从节点查不到,先检查从节点日志,常见原因是REDIS_MASTER_HOST写错、主从密码不一致,或者两个节点不在同一个 Docker 网络里。
5.2 模拟主节点宕机
故障转移演练的核心路径是:停掉主节点,观察哨兵的行为,验证新主节点被选出,最后让旧主节点重新加入。
先停掉主节点容器,模拟宕机:
docker stop redis-master然后立刻去哨兵节点查看日志。等十几秒(具体时间取决于down-after-milliseconds配置,默认 5000ms 左右),你应该在哨兵日志里看到主观下线、客观下线、选举、切换这一连串事件。
用docker logs跟踪哨兵-1的日志:
docker logs sentinel-1 --tail 50你会看到类似下面的关键日志:
+sdown master mymaster 172.19.0.x 6379 +odown master mymaster 172.19.0.x 6379 #quorum 2/2 +try-failover master mymaster 172.19.0.x 6379 +switch-master mymaster 172.19.0.x 6379 172.19.0.y 6379这几条日志的含义是:
+sdown:哨兵主观认为主节点不可用+odown:哨兵确认超过 quorum 数量的哨兵都认为主节点不可用,进入客观下线+try-failover:开始尝试执行故障转移+switch-master:主节点已经从旧 IP 成功切换到新 IP
+switch-master是最关键的日志,看到它说明哨兵已经完成了故障转移。现在再去任意一个哨兵节点查询当前主节点地址:
docker exec -it sentinel-1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster返回的 IP 应该已经变为某个从节点的 IP。用info replication查看这个新主节点的角色,确认它已经变成role:master。
5.3 旧主节点恢复后的处理
旧主节点恢复后会发生什么?很多人以为它会重新当回主节点,这是一个常见误解。事实上,旧主节点重新启动后,会发现自己已经不是 master,哨兵会把它作为从节点加入现有集群,复制新主节点的数据。
把旧主节点拉起来:
docker start redis-master过几秒后进入旧主节点容器,查看它的角色:
docker exec -it redis-master redis-cli -a redis123 info replication你会看到类似:
# Replication role:slave master_host:172.19.0.y master_port:6379也就是说,旧主节点虽然服务名还叫redis-master,但在当前集群拓扑里已经变成新主的从节点了。这一点在后续维护时一定要牢牢记住:容器名只是标识,不代表角色。如果你在 Compose 里仍然通过服务名redis-master访问容器,获得的可能是一个从节点,读写路径会变得混乱。
演练完成后,如果你希望让原来的主节点重新成为真正的主节点,需要手动干预,比如在当前主节点上执行FAILOVER命令(Redis 7.0 支持)或者用哨兵的SENTINEL FAILOVER mymaster命令强制触发一次故障转移。不过生产环境一般不推荐没事就切来切去,毕竟每次故障转移都可能带来短暂的写不可用窗口。
6. 常见问题排查与避坑清单
6.1 高概率踩坑问题速查表
我把这半年用这套镜像过程中遇到的典型问题和解决办法整理成了表格,方便你遇到问题时直接查。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
Sentinel 日志出现-denied或# NOAUTH | 哨兵访问主从节点时认证失败 | 确保REDIS_MASTER_PASSWORD与 Redis 节点REDIS_PASSWORD一致 |
从节点一直显示down状态 | 主从密码不同或主节点地址不对 | 检查REDIS_MASTER_PASSWORD、REDIS_MASTER_HOST和端口 |
| Sentinel 无法相互发现 | 哨兵节点间网络隔离或sentinel announce-ip未配置 | 确保所有哨兵在同一 Docker 网络,必要时配置REDIS_SENTINEL_ANNOUNCE_IP |
| 故障转移后应用仍连旧主节点 | 客户端未配置哨兵模式,仍直连固定地址 | 客户端改为通过哨兵发现地址,或使用支持哨兵的客户端连接方式 |
| 容器启动后立即退出 | 卷目录权限不足 | 手动创建宿主机目录并赋予当前用户写权限 |
| 主从切换后旧主节点重新加入却不同步 | 主从密码配置漏了旧主节点 | 在旧主节点配置REDIS_MASTER_PASSWORD后重启 |
Sentinel 状态显示master_link_down | 哨兵与主节点之间网络抖动 | 检查哨兵所在容器与 Redis 容器是否能 ping 通 |
6.2 关于密码和认证的几个细节
密码配置是我踩过最多坑的地方。这套镜像的认证体系涉及三层:Redis 节点之间的认证、客户端访问 Redis 的认证、哨兵访问 Redis 的认证。三层密码如果不一致,就会出现“部分功能正常,但故障转移时神秘失败”的局面。
REDIS_PASSWORD是节点对外提供认证的密码,也就是客户端连接时需要用到的密码。在主从节点上都要设置,并且要保证一致。REDIS_MASTER_PASSWORD是从节点和哨兵用来连接主节点的密码。如果你只给主节点配了REDIS_PASSWORD,没给从节点配REDIS_MASTER_PASSWORD,从节点同步数据时就会被主节点拒绝。
第二个细节是 Bitnami 镜像默认会启动 redis-cli 的--no-auth-warning,所以你在日志里不会看到“Warning: Using a password with '-a'..."这种提示,这不代表密码没生效。在运维时手写redis-cli -a xxx虽然方便,但要注意 shell 历史记录里会留下明文密码。我建议在非交互式脚本里用REDISCLI_AUTH环境变量来传密码,比如:
docker exec -it redis-master env REDISCLI_AUTH=redis123 redis-cli info replication这样密码不会出现在命令行参数里,避免通过ps看到明文。
第三个细节:如果生产环境安全性要求高,建议为 Sentinel 也单独设置密码。Bitnami 镜像提供REDIS_SENTINEL_PASSWORD环境变量。但要注意,设置后客户端连接哨兵也需要认证,并且哨兵之间的通信同样需要认证,这个信息要同步到所有哨兵节点。
6.3 不要忽略哨兵自身的脚本化守护
部署完哨兵后,有个容易被忽略的问题:三个哨兵容器部署在同一台物理机上,那这台物理机挂了,所有哨兵一起挂,Redis 高可用就是空话。这不算 Bitnami 镜像的问题,而是部署拓扑的问题。单机环境下做的是功能验证,生产环境务必把哨兵节点分布到不同主机、不同机柜甚至不同可用区。
另外,哨兵本身也需要被守护。在容器环境里,建议给哨兵容器配置restart: unless-stopped,这样一旦 Sentinel 进程异常退出或被宿主机杀掉,Docker 会自动把它拉起来。我在配置生产环境的 Compose 时,通常还会给 Redis 节点设置更精细的restart策略,但至少不能什么都不配,否则一次系统重启可能让你整个高可用体系全部失效。
7. 应用侧接入与后续扩展
7.1 Spring Boot / Java 客户端的哨兵接入
部署哨兵集群最终是要给应用提供高可用的 Redis 读写能力。以 Spring Boot 为例,接入哨兵模式非常简单,不需要自己实现复杂的故障转移逻辑,配置里指定哨兵地址和主节点名称即可。
在application.yml里这样写:
spring: data: redis: password: redis123 sentinel: master: mymaster nodes: - 127.0.0.1:26379 timeout: 3000ms注意,nodes配置的是哨兵的地址和端口,不是 Redis 主节点的地址。客户端启动时会先连上哨兵,通过SENTINEL get-master-addr-by-name mymaster拿到当前主节点地址并建立连接池。当主节点发生故障转移后,客户端也能通过哨兵感知到新主节点地址,自动切换。这就是哨兵模式对应用最友好的地方。
在 Java 代码里,你依然使用StringRedisTemplate或RedisTemplate操作数据,底层连接工厂会自动处理哨兵逻辑,业务代码完全不用关心当前谁是主节点。
7.2 哨兵模式下的缓存与分布式锁注意事项
部署完哨兵后,我发现有些同学会误以为“高可用 = 数据绝对安全”,于是在哨兵模式下也把 Redis 当分布式锁的最终防线,但这其实是有一点风险的。哨兵模式解决的是“单点进程故障”问题,它不能解决“主从异步复制导致的数据丢失”问题。
举个例子:主节点刚处理完一条SET lock:order 2024-05-01 10:00:00 EX 10的写请求,还没来得及把数据同步给从节点,主节点就宕机了。哨兵此时把从节点提升为新主,但这个锁数据在新主上是不存在的。另一个线程过来尝试获取同一个锁,可能直接就成功了,于是两个线程同时持锁。在分布式锁这种对强一致性要求很高的场景里,这属于不可接受的隐患。
如果你确实需要在 Redis 上实现可靠的分布式锁,应该考虑 Redis 官方的 Redlock 算法,或者直接评估上 Redis Cluster 甚至其他带强一致语义的存储,而不是指望哨兵模式解决问题。哨兵模式适合的场景是缓存、非关键性数据存取、可容忍短时间丢失的读写分离架构。
如果只是缓存场景,我还建议你关注一下缓存治理的问题。使用哨兵模式后,缓存击穿、缓存穿透、缓存雪崩这些老问题依然存在,并不会因为高可用就自动消失。我在使用中会把“哨兵故障转移”和“本地缓存兜底”结合,应用层维护一个极短生命周期的本地缓存,当 Redis 切换主节点的几十毫秒内请求失败时,直接用本地缓存兜底,显著降低对业务的影响。
7.3 下一步:监控与可观测性
哨兵集群不是部署完就能彻底不管了,因为它自身也会有各种异常状态。我建议至少监控三项指标:哨兵与主节点的连接状态、Redis 主从复制延迟、故障转移事件发生次数。当故障转移频繁发生时,你的主从集群可能已经处在某种亚健康状态,这时候要考虑机器负载、带宽、磁盘等问题,而不是只盯着哨兵的配置。
在实际操作中,我是通过在哨兵容器里执行SENTINEL info mymaster来查看可观测指标的。这个命令会返回主从状态、副本数量、各哨兵对主节点的看法等详细信息。比如master0行里的sdown或odown字段,能直观反映哨兵眼中主节点的状态。把这些信息采集到 Prometheus 这类监控系统里,再配上告警规则,才算把一个完整的 Redis 高可用方案闭环。
如果你对可观测性要求更高,可以考虑在应用侧使用 Redis 官方推荐的redis_exporter采集指标,配合 Grafana 面板展示。不过那是另外一个话题了,如果你有兴趣,之后可以单独写一篇聊聊。
我把这个流程走通之后,最大的感觉是:哨兵模式的门槛不在配置文件本身,而在整个部署拓扑和故障转移机制的深刻理解。用 Bitnami 镜像能帮你省去大部分配置文件的手工维护,但它不会替你解决设计层面的问题,比如哨兵分布、密码策略、客户端接入方式。只有在部署、验证、演练、监控四个环节都跑通的前提下,这套方案才会真正变成你基础设施里可靠的一环。
最后再分享一个小技巧:在本地或者测试环境做演练时,不要只停一个节点。你可以试着同时停掉一个哨兵和一个从节点,看看集群是否还能正常完成一次故障转移,再试着把两个哨兵同时停掉,感受一下 quorum 机制是如何阻止误切换的。这种“故意搞破坏”的练习,能帮你积累很多运行时的直觉,远比反复读文档更有效。