org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica.
时间: 2025-03-19 08:23:44 浏览: 227
<think>嗯,用户遇到了Redis的READONLY错误,想用Spring Data Redis和Lettuce解决这个问题。首先,我需要回忆一下这个错误的原因。根据引用内容,这个错误是因为尝试往只读副本(replica)执行写操作导致的。Redis的主从架构中,从节点默认是只读的,用户可能在配置中错误地将应用指向了从节点,或者主节点故障后没有正确切换。
接下来,我需要考虑可能的解决方案。首先,检查Redis连接配置是否正确,确保写操作发送到主节点。如果使用了集群或哨兵模式,可能需要检查配置是否正确。然后,可以尝试在从节点设置`replica-read-only no`,不过默认是yes,修改这个可能会有风险,需要谨慎。另外,Lettuce客户端可能需要配置自动拓扑刷新,以便在主从切换时自动更新连接。
用户提到使用的是Spring Data Redis和Lettuce,所以需要具体到这些技术的配置。例如,检查Spring的配置文件中的spring.redis.url或cluster/nodes设置是否正确。Lettuce的配置可能需要开启定时刷新拓扑和自适应拓扑更新。
另外,异常处理方面,建议在代码中添加重试机制,比如使用Spring Retry,在捕获到RedisReadOnlyException时进行重试,或者降级处理。同时,监控和告警也是必要的,及时发现主从切换或配置错误的情况。
还要考虑版本兼容性问题,确保Spring Data Redis和Lettuce的版本支持自动拓扑刷新。如果用户的环境是哨兵模式,需要配置哨兵节点的信息,让客户端能够自动发现主节点。
总结下来,解决步骤应该是:1. 检查连接配置,确保写操作到主节点;2. 配置Lettuce自动刷新拓扑;3. 必要时调整Redis副本的只读设置;4. 代码中添加异常处理机制;5. 设置监控告警。同时,引用中提到的错误信息需要对应到具体的解决方案,比如引用[1]和[2]提到的异常,可能需要检查配置和拓扑刷新。</think>### Redis主从架构下出现"READONLY"错误的解决方案
当使用Spring Data Redis连接Redis主从集群时,若出现`io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica`错误,可通过以下步骤排查解决:
---
#### 一、根本原因分析
1. **主从拓扑识别错误**:客户端错误地将写请求发送到从节点(Replica),而Redis从节点默认开启只读模式(`replica-read-only yes`)[^1][^2]
2. **主节点故障转移未完成**:主节点宕机后,集群尚未完成故障转移或客户端未及时感知新拓扑
3. **连接池配置错误**:连接池中的连接指向了错误的节点类型
---
#### 二、解决方案步骤
1. **验证Redis节点角色**
```bash
# 连接到疑似主节点的Redis实例
redis-cli -h {host} -p {port}
> INFO replication
# 查看role字段确认角色(master/replica)
```
2. **Spring Boot配置检查**
```properties
# 确保连接主节点(单节点模式)
spring.redis.host=主节点IP
spring.redis.port=6379
# 哨兵模式正确配置
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=哨兵节点1:26379,哨兵节点2:26379
```
3. **Lettuce客户端配置优化**
```java
@Configuration
public class RedisConfig {
@Bean
public LettuceClientConfigurationBuilderCustomizer lettuceCustomizer() {
return clientConfigurationBuilder -> {
clientConfigurationBuilder
.clientOptions(ClientOptions.builder()
.autoReconnect(true)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
.build())
.commandTimeout(Duration.ofSeconds(30));
};
}
}
```
4. **启用拓扑刷新(关键配置)**
```properties
# 开启自动拓扑刷新(Spring Boot 2.3+)
spring.redis.lettuce.cluster.refresh.adaptive=true
spring.redis.lettuce.cluster.refresh.period=2000
```
5. **应急处理方案**
```java
@Retryable(value = RedisReadOnlyException.class,
maxAttempts = 3,
backoff = @Backoff(delay = 100))
public void writeOperation(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
```
---
#### 三、预防措施
1. **网络拓扑监控**:通过`RedisClusterClient#getPartitions()`实时获取集群分区信息
2. **连接验证机制**:
```java
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.ping();
return null;
});
```
3. **版本兼容性检查**:
- Lettuce ≥ 5.3.4.RELEASE
- Spring Boot ≥ 2.3.0.RELEASE
---
#### 四、高级场景处理
对于**混合部署环境**(部分节点在K8s集群,部分在物理机),需要特别注意:
```properties
# 强制指定首选地址类型
spring.redis.lettuce.cluster.preferred-addr-type=PUBLIC
```
---
阅读全文
相关推荐











