Redis Sentinel是官方提供的高可用方案,它通过一组独立的哨兵进程监控Redis主从节点,在主节点不可用时自动完成故障转移。很多人在搭建好Sentinel集群后,把客户端当成单机Redis来连,结果主从切换之后应用还在连旧的主节点,读写全部报错。问题的根源不在Sentinel本身,而在于客户端必须以Sentinel的方式接入,才能实时感知主节点的变化。本文就来详细讲讲Redis Sentinel客户端的正确配置姿势。

一、先搞清楚客户端与Sentinel的交互机制
理解配置之前,必须明白客户端到底是怎么通过Sentinel找到主节点的。整个过程可以概括为三步:发现哨兵、查询主节点地址、直连主节点读写。
第一步是发现哨兵。客户端配置文件里写的不是Redis节点的地址,而是Sentinel节点的地址,通常至少写两到三个哨兵的IP和端口。客户端会依次尝试连接这些哨兵,只要有一个连通就能继续工作,这就是为什么生产环境建议哨兵至少部署三个节点,避免单点故障。
第二步是查询主节点地址。客户端连上哨兵后,会发送SENTINEL get-master-addr-by-name mymaster命令,哨兵返回当前主节点的IP和端口。注意这里的mymaster是主节点名称,必须和哨兵配置文件里的master name完全一致,大小写敏感,这是最常见的配置错误之一。
第三步是直连主节点。客户端拿到主节点地址后,后续的读写操作都直接发往主节点,数据面流量并不经过哨兵。哨兵只负责控制面的信息查询和事件通知。当故障转移发生时,客户端通过订阅哨兵的+switch-master事件或者重新查询,感知到主节点变化,然后建立新连接。
另外需要了解一点:从节点地址不是必须获取的。只有在客户端需要做读写分离时,才会额外执行SENTINEL slaves mymaster或SENTINEL replicas mymaster来获取从节点列表。默认情况下,写和读都走主节点,这是最简单也最安全的模式。
二、Java客户端的配置方法
Java生态里常用的客户端是Jedis和Lettuce,两者的Sentinel配置方式有明显差异,下面分别给出示例。
先看Jedis。Jedis提供了JedisSentinelPool,它会自动处理主节点切换,使用时从连接池里拿连接即可,不需要自己判断当前主节点是谁:
Set<String> sentinels = new HashSet<>();
sentinels.add(new HostAndPort("192.168.1.10", 26379).toString());
sentinels.add(new HostAndPort("192.168.1.11", 26379).toString());
sentinels.add(new HostAndPort("192.168.1.12", 26379).toString());
// 第一个参数是master名称,必须与哨兵配置中一致
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,
jedisPoolConfig, 2000, "yourpassword");
try (Jedis jedis = pool.getResource()) {
jedis.set("demo:key", "hello");
System.out.println(jedis.get("demo:key"));
}
这段代码有几个细节值得注意。首先是超时时间,连接哨兵和连接Redis的超时建议都设置在秒级以内,故障转移期间旧的连接会大量超时,太长的超时会拖慢应用的响应。其次是密码问题,如果Redis数据节点设置了requirepass,构造函数里要传入对应密码;如果哨兵本身也设置了密码,还需要通过系统属性或HostAndPort相关配置提供哨兵认证信息,Jedis高版本支持在每个哨兵地址后附加密码。
再看Lettuce,它是Spring Boot 2.x之后默认的Redis客户端,底层基于Netty,支持异步和连接复用。Lettuce的Sentinel配置通过RedisURI构建:
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import java.time.Duration;
RedisURI uri = RedisURI.builder()
.withPassword("yourpassword".toCharArray())
.withSentinelMasterId("mymaster")
.withSentinel("192.168.1.10", 26379)
.withSentinel("192.168.1.11", 26379)
.withSentinel("192.168.1.12", 26379)
.withTimeout(Duration.ofSeconds(2))
.build();
RedisClient client = RedisClient.create(uri);
StatefulRedisConnection<String, String> conn = client.connect();
conn.sync().set("demo:key", "hello");
System.out.println(conn.sync().get("demo:key"));
Lettuce相比Jedis的优势在于连接线程安全,单个连接可以被多个线程共享,故障转移时会自动重新解析主节点地址。如果哨兵节点也有密码,可以用withSentinelPassword单独指定,这一点比Jedis的配置更清晰。
Spring Boot场景下则更简单,直接在配置文件中声明即可:
spring:
redis:
password: yourpassword
sentinel:
master: mymaster
nodes:
- 192.168.1.10:26379
- 192.168.1.11:26379
- 192.168.1.12:26379
timeout: 2000
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 2
这里容易忽略的一点是Spring Boot 2.x之后spring.redis.sentinel.nodes里写的是哨兵地址,不再是数据节点地址。有些人把主节点的6379端口写进去,启动时会报Cannot resolve master之类的错误。另外spring.redis.host和spring.redis.sentinel不能同时生效,配了哨兵就要把单机地址配置去掉,否则优先级混乱会导致连接行为不符合预期。
三、其他语言客户端的配置示例
Python客户端redis-py从3.x版本开始,创建客户端时传入sentinels参数即可启用哨兵模式:
from redis.sentinel import Sentinel
sentinel = Sentinel(
[("192.168.1.10", 26379),
("192.168.1.11", 26379),
("192.168.1.12", 26379)],
socket_timeout=2
)
# 获取主节点连接,写操作使用
master = sentinel.master_for("mymaster", password="yourpassword")
master.set("demo:key", "hello")
# 获取从节点连接,可用于只读操作
replica = sentinel.slave_for("mymaster", password="yourpassword")
print(replica.get("demo:key"))
master_for返回的对象底层会维护连接池,并在连接失效时重新向哨兵查询主节点地址,不需要应用层手动刷新。如果做读写分离,要清楚一个前提:主从复制是异步的,从节点读到的数据可能存在延迟,对一致性敏感的业务不适合走从节点。
Go语言常用go-redis库,配置方式同样以哨兵地址为核心:
import (
"context"
"github.com/redis/go-redis/v9"
)
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{
"192.168.1.10:26379",
"192.168.1.11:26379",
"192.168.1.12:26379",
},
Password: "yourpassword",
DB: 0,
})
val, err := rdb.Get(context.Background(), "demo:key").Result()
go-redis还提供了NewFailoverClusterClient,适用于多主多从的哨兵管理多套主从的场景,配置上会更加复杂一些。对于单主多从的经典架构,NewFailoverClient已经够用。
四、生产环境的关键配置与常见坑
配置能跑通只是第一步,生产环境还需要关注下面这些细节。
第一是故障转移期间的重试策略。哨兵完成一次故障转移通常需要十几秒到几十秒,期间客户端的写请求会失败。应用层应该对写操作做有限次数的重试,并配合熔断或降级逻辑,避免故障期间请求堆积。同时建议开启客户端的自动重连功能,Lettuce和redis-py默认都支持。
第二是连接池参数。故障转移后旧连接全部失效,连接池需要有快速淘汰坏连接的机制。Jedis建议配置testWhileIdle和合理的minEvictableIdleTimeMillis,让空闲检测及时发现无效连接。池子最大连接数要根据业务并发量评估,一般线上应用设置16到64比较常见。
第三是master名称一致性。客户端配置的名称必须和每个哨兵配置文件里sentinel monitor指令定义的名称一致。如果多个哨兵配置不一致,不同哨兵会返回不同的监控结果,客户端行为会非常诡异,排查起来很痛苦。上线前用redis-cli -p 26379 sentinel get-master-addr-by-name mymaster逐个检查每个哨兵的返回值,确认一致。
第四是网络分区下的订阅连接问题。客户端除了查询主节点地址,还会订阅哨兵的发布订阅频道来第一时间感知切换。如果客户端到哨兵之间的网络不稳定,订阅连接断开后客户端会退化为轮询查询模式,感知切换的延迟会变长。因此哨兵节点和应用服务器之间的网络质量同样重要。
第五是认证配置的完整性。较新的Redis版本中,数据节点和哨兵节点可以分别设置密码,客户端需要同时配置两套认证信息。如果只配了数据节点密码,连接哨兵时可能被拒绝,报NOAUTH错误;反过来只配哨兵密码,查询主节点地址成功但连数据节点失败。遇到认证相关报错时,先把两层密码配置核对一遍。
最后提一个排查思路:当应用出现间歇性的Redis连接异常时,先看哨兵侧的日志,确认是否发生过+sdown或+switch-master事件,再检查客户端是否正确加载了哨兵配置。很多时候问题出在配置没有真正生效,比如Spring Boot中依赖版本不匹配导致哨兵配置被忽略,打一段连接日志确认实际连接的地址,能节省大量排查时间。
总结一下,Sentinel客户端配置的核心是把哨兵地址交给客户端、保证master名称一致、配置好双端认证、合理设置超时和重连参数。把这些环节都落实到位,主从切换对应用来说就是几秒钟的短暂抖动,而不是一次线上事故。
Redis Sentinel客户端配置高可用修改时间:2026-09-03 19:59:27