Spring Data Redis,yml 配置中添加 Redis 主从集群配置,一主二从集群,没有哨兵
在 Spring Data Redis 中,针对没有哨兵(Sentinel)的一主二从 架构,配置方式主要取决于你使用的客户端(Lettuce 或 Jedis)以及你是否需要读写分离。
通常情况下,Spring Boot 会自动发现主从关系,但如果需要精细控制(如强制从从节点读取),则需要通过 Java 配置类配合 yml 来实现。
以下是基于 Lettuce 客户端(Spring Boot 2.x/3.x 默认)的完整配置方案。
1. application.yml 配置
在没有哨兵的情况下,我们通常手动指定所有节点(主节点 + 从节点),或者仅指定主节点(Lettuce 会自动通过 CLUSTER NODES 或 INFO REPLICATION 发现从节点,但在静态主从模式下建议显式列出)。
yaml
spring:
redis:
# 默认连接的主节点 IP(用于初始化连接)
host: 192.168.1.100
port: 6379
password: your_password
database: 0
timeout: 3000ms
# 关键配置:Lettuce 连接池与读写策略
lettuce:
pool:
max-active: 20 # 最大连接数
max-idle: 10 # 最大空闲连接
min-idle: 5 # 最小空闲连接
max-wait: 2000ms # 连接超时时间
# 拓扑刷新策略(可选,用于动态感知节点变化)
cluster:
refresh:
adaptive: true
# 如果你使用的是 Jedis,配置结构会有所不同(见下文补充)
jedis:
pool:
max-active: 20
max-idle: 10
min-idle: 5
注意 :在纯主从模式(非集群模式)下,
spring.redis.cluster.nodes配置项通常不生效或不需要配置。我们需要在 Java 配置类中构建静态的主从拓扑。
2. Java 配置类(实现读写分离)
仅靠 yml 配置,Spring 默认所有请求都会发往主节点。为了实现**"主写从读"**,你需要创建一个配置类,利用 LettuceClientConfiguration 设置读取策略。
这里演示如何配置 优先从从节点读取 (REPLICA_PREFERRED):
java
import io.lettuce.core.ReadFrom;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import java.util.Arrays;
@Configuration
public class RedisMasterReplicaConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// 1. 定义主从节点拓扑结构
// 格式:host:port,这里手动指定一主二从
RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(
Arrays.asList(
host + ":" + port, // 主节点
"192.168.1.101:6380", // 从节点 1
"192.168.1.102:6381" // 从节点 2
)
);
config.setPassword(RedisPassword.of(password));
// 2. 配置 Lettuce 客户端行为
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
// 核心配置:优先从从节点读取数据
// 如果从节点不可用,会自动回退到主节点
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
// 3. 创建连接工厂
return new LettuceConnectionFactory(config, clientConfig);
}
}
3. 核心配置说明
| 配置项/策略 | 说明 |
|---|---|
| RedisStaticMasterReplicaConfiguration | 用于在非哨兵模式下,手动指定主从节点列表。Spring 会维护这个静态列表。 |
| ReadFrom.REPLICA_PREFERRED | 推荐。优先从 Slave 读取,如果 Slave 挂了,自动切回 Master,保证高可用。 |
| ReadFrom.REPLICA | 强制只从 Slave 读取。如果 Slave 全挂,会抛出异常(不推荐,除非你有严格的一致性要求且能容忍报错)。 |
| ReadFrom.UPSTREAM | 默认行为。只从 Master 读取(即没有读写分离)。 |
4. 补充:如果你使用的是 Jedis
虽然 Lettuce 是默认推荐,但如果你使用的是 Jedis,配置逻辑类似,但类名不同:
java
// Jedis 版本的配置思路
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
// ... 在 @Bean 中
RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(...);
// Jedis 同样支持 readFrom 设置,但通常通过 JedisClientConfiguration 构建
JedisClientConfiguration clientConfig = JedisClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
return new JedisConnectionFactory(config, clientConfig);
5. 验证配置是否生效
启动项目后,你可以通过以下方式验证:
- 查看日志:启动时观察控制台日志,看是否成功连接到了所有指定的节点。
- 压测观察 :在主节点和从节点的服务器上使用
tcpdump或 Redis 监控工具,观察GET请求是否分发到了从节点(6380, 6381),而SET请求是否只在主节点(6379)执行。 - Redis 命令 :在 Redis 主节点执行
info replication,确保connected_slaves数量为 2,且状态为online。
Spring Data Redis,yml 配置中添加 Redis 哨兵集群配置
在 Spring Data Redis 中配置 Redis 哨兵(Sentinel)集群,核心在于告诉 Spring Boot:"不要直接连 Redis 主节点,而是去问哨兵节点,当前的老大(Master)是谁"。
以下是基于 Spring Boot 2.x/3.x 和默认客户端 Lettuce 的完整配置方案。
1. application.yml 配置
这是最关键的一步。你需要配置哨兵节点的列表以及主节点在哨兵配置中定义的逻辑名称 (通常叫 mymaster,但具体取决于你的 Redis 服务端配置)。
yaml
spring:
redis:
# 1. 哨兵配置核心
sentinel:
master: mymaster # 【重要】这是 Redis 主节点在 Sentinel 配置中的逻辑名称,不是 IP
nodes: # 哨兵节点的 IP:Port 列表(建议配置所有哨兵,用逗号分隔或列表格式)
- 192.168.1.100:26379
- 192.168.1.101:26379
- 192.168.1.102:26379
# 2. 认证与基础配置
password: your_redis_password # Redis 主从节点的密码
# sentinel-password: your_sentinel_password # 如果哨兵节点单独设置了密码,需配置此项(Spring Boot 2.4+)
database: 0
timeout: 3000ms
# 3. 连接池配置 (Lettuce)
lettuce:
pool:
max-active: 20 # 最大连接数
max-idle: 10 # 最大空闲连接
min-idle: 5 # 最小空闲连接
max-wait: 2000ms # 获取连接的最大等待时间
⚠️ 注意事项:
master字段 :必须与 Redis 服务端sentinel.conf中sentinel monitor <master-name> ...这一行的<master-name>完全一致。nodes字段 :这里填写的是哨兵的端口(默认 26379),而不是 Redis 数据节点的端口(默认 6379)。
2. 实现读写分离(Java 配置)
仅配置 yml 文件,Spring Boot 默认会连接 Master 进行读写。如果你希望利用从节点(Slave)分担读流量,需要添加一个简单的 Java 配置类来指定读取策略。
java
import io.lettuce.core.ReadFrom;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
@Configuration
public class RedisSentinelConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory(RedisSentinelConfiguration sentinelConfig) {
// 配置 Lettuce 客户端:优先从从节点读取数据
// 如果从节点不可用,会自动降级回主节点
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
}
3. 配置参数详解
| 配置项 | 说明 |
|---|---|
spring.redis.sentinel.master |
逻辑名称 。Spring 会问哨兵:"谁是 mymaster 的老大?",哨兵会返回当前的 Master IP。 |
spring.redis.sentinel.nodes |
哨兵列表。Spring 会随机连接其中一个哨兵来获取 Master 地址。如果连接失败,会自动尝试列表中的下一个。 |
spring.redis.password |
数据节点密码。用于连接 Redis Master/Slave 的密码。 |
spring.redis.sentinel.password |
哨兵节点密码 。如果你的哨兵进程单独设置了 requirepass,需要配置此项。 |
4. 常见问题排查
-
启动报错
Cannot get master address from sentinel:- 检查
master名称是否拼写正确(通常默认是mymaster,但也可能是master1等)。 - 检查防火墙是否开放了哨兵端口(26379)。
- 确保 Spring Boot 服务器能
ping通哨兵服务器。
- 检查
-
连接到了错误的 IP:
- Redis 哨兵返回的是 Redis 实例配置的 IP。如果 Redis 部署在 Docker 或 NAT 网络后,确保 Redis 实例配置的
bind-ip和哨兵能识别的 IP 是 Spring Boot 服务器能访问的。
- Redis 哨兵返回的是 Redis 实例配置的 IP。如果 Redis 部署在 Docker 或 NAT 网络后,确保 Redis 实例配置的
-
Spring Boot 3.x 差异:
- Spring Boot 3.x 的配置前缀依然是
spring.data.redis(在 yml 中通常简写为spring.redis),配置结构基本保持不变。
- Spring Boot 3.x 的配置前缀依然是
Spring Data Redis,yml 配置中添加 Redis 分片集群配置
在 Spring Data Redis 中配置 Redis 分片集群(Redis Cluster),核心在于告诉客户端集群的"种子节点"。客户端(默认是 Lettuce)连接后会自动发现整个集群拓扑结构。
以下是基于 Spring Boot 和 Lettuce 客户端的完整配置方案。
1. application.yml 配置
这是最基础也是最重要的部分。你需要列出部分节点(通常建议列出所有主节点,或者至少 3 个节点),Spring Boot 会自动识别这是一个集群环境。
yaml
spring:
redis:
# 1. 集群配置核心
cluster:
# 节点列表(逗号分隔或数组格式)
# 注意:这里填写的是 Redis 数据节点的 IP:Port,不是哨兵端口
nodes:
- 192.168.1.100:7001
- 192.168.1.100:7002
- 192.168.1.100:7003
- 192.168.1.100:7004
- 192.168.1.100:7005
- 192.168.1.100:7006
# 2. 认证与基础配置
password: your_redis_password # 集群统一密码
timeout: 3000ms # 命令超时时间
# 3. Lettuce 客户端配置(推荐)
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 2000ms
# 集群拓扑刷新配置(应对主从切换)
cluster:
refresh:
adaptive: true # 自适应刷新:遇到 MOVED/ASK 重定向时自动更新拓扑
period: 60s # 定期刷新:每 60 秒强制刷新一次拓扑(作为兜底)
💡 关键点说明:
spring.redis.cluster.nodes:只要配置了此项,Spring Boot 就会自动启用RedisClusterConfiguration,无需手动指定host和port。- 节点数量:不需要配置所有 6 个节点,配置 3 个主节点通常就足够 Lettuce 发现全貌,但为了高可用,建议配置所有节点。
adaptive: true:非常重要。当集群发生主从切换(Failover)时,客户端缓存的槽位(Slot)映射会失效。开启此配置后,Lettuce 收到MOVED异常时会自动更新路由表,无需重启服务。
2. 实现读写分离(Java 配置)
与哨兵模式类似,默认情况下 Spring Data Redis 会优先读写主节点。如果你希望利用从节点分担读流量,需要添加配置类。
java
import io.lettuce.core.ReadFrom;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
@Configuration
public class RedisClusterConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory(RedisClusterConfiguration clusterConfig) {
// 配置 Lettuce 客户端:优先从从节点读取数据
// 如果从节点不可用,会自动降级回主节点
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
return new LettuceConnectionFactory(clusterConfig, clientConfig);
}
}
3. 代码使用注意事项(跨 Slot 问题)
在分片集群中使用 RedisTemplate 时,有一个巨大的"坑"需要注意:多 Key 操作。
- 单 Key 操作(推荐) :
set(key, value),get(key),hget(key, field)等完全没问题,客户端会自动计算 Hash Slot 并路由到正确节点。
- 多 Key 操作(限制) :
mget(key1, key2),mset(key1, val1, key2, val2)等操作,要求所有 Key 必须落在同一个 Slot 上。- 如果
key1在节点 A,key2在节点 B,直接调用会报错CROSSSLOT Keys in request don't hash to the same slot。
解决方案:使用 Hash Tag {}
如果你必须对多个 Key 进行批量操作,可以使用 {} 包裹 Key 的公共部分,强制它们落入同一个 Slot。
java
// 错误示范:可能跨 Slot
redisTemplate.opsForValue().multiGet(Arrays.asList("user:1001", "user:1002"));
// 正确示范:强制使用相同的 Slot(取 {} 内的内容计算 hash)
// 这两个 Key 都会根据 "common_group" 计算槽位,从而落在同一个节点
redisTemplate.opsForValue().multiGet(Arrays.asList("{common_group}:user:1001", "{common_group}:user:1002"));
4. 配置参数详解
| 配置项 | 说明 |
|---|---|
spring.redis.cluster.nodes |
种子节点列表 。客户端连接任意一个节点后,会执行 CLUSTER NODES 命令获取完整拓扑。 |
spring.redis.lettuce.cluster.refresh.adaptive |
自适应刷新。开启后,当遇到重定向异常时,自动触发拓扑更新,是生产环境必备配置。 |
spring.redis.lettuce.cluster.refresh.period |
定期刷新。作为双重保险,定期后台刷新拓扑结构。 |
spring.redis.password |
集群密码。Redis Cluster 通常所有节点配置相同的密码。 |
5. 常见报错排查
-
MOVED xxx 192.168.x.x:700x:- 这是正常现象,表示客户端请求的节点不是该 Key 的负责节点,节点告诉客户端"去那边找"。
- 解决 :确保开启了
adaptive: true,Lettuce 会自动处理。如果一直报错,检查网络是否通畅,或者客户端是否无法连接新的 IP(如内网 IP 映射问题)。
-
CROSSSLOT Keys in request don't hash to the same slot:- 原因 :执行了
mget、mset或事务操作,但涉及的 Key 分布在不同的槽位。 - 解决 :使用
{tag}语法强制 Key 落在同一槽位,或者在代码中循环单 Key 操作(性能稍低)。
- 原因 :执行了
-
连接超时:
- 检查 Redis 节点的
cluster-announce-ip配置。如果 Redis 在 Docker 中,必须配置该参数为宿主 IP,否则客户端拿到的拓扑信息是容器内网 IP,导致无法连接。
- 检查 Redis 节点的