# 彻底解决 RedisReadOnlyException:从「只读从节点」到「独立主节点」的实战指南

彻底解决 RedisReadOnlyException:从「只读从节点」到「独立主节点」的实战指南

在 Spring Boot 项目集成 Redis 时,很多开发者都会遇到 io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica. 这个异常。看似简单的「写入被拒绝」,背后却涉及 Redis 主从架构的核心逻辑。本文将结合实际操作场景,从异常分析、问题排查到最终落地解决方案,一步步带你彻底解决这个问题,甚至将原本的只读从节点改造为独立可读写的主节点。

一、异常背景与现场还原

1. 异常现象

Spring Boot 项目启动失败,抛出核心异常:

plaintext

复制代码
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.

从异常堆栈可以清晰看到,项目在执行 RedisCache.setCacheObject() 写入操作时被拒绝,根源是连接的 Redis 节点为「只读副本」(从节点)。

2. 前期操作现场还原

为了排查问题,我们在 Windows 终端尝试连接 Redis 服务器,成功连接 Redis 节点,并通过 INFO replication 命令排查节点信息:

运行

复制代码
C:\Users\admin>redis-cli -h xxx.xx.121.134 -p 6379
xxx.xx.121.134:6379> INFO replication

返回结果暴露了核心问题,关键信息提炼如下:

配置项 取值 含义解读
role slave 当前连接的是 Redis 从节点(只读副本)
master_host xxx.xx.232.83 对应的主节点 IP 地址
master_port 25154 对应的主节点端口(非默认 6379)
master_link_status down 从节点与主节点连接已断开,无法同步数据
slave_read_only 1 从节点只读模式开启(1 = 开启,0 = 关闭)
connected_slaves 0 无下级从节点,当前节点为最末端副本

二、异常核心原理解析

要解决这个异常,首先要理解 Redis 主从复制架构的核心设计:

  1. 主节点(Master) :具备「读写权限」,负责接收所有写操作(SETHSET 等),同时将数据同步到所有从节点,是整个架构的核心数据节点。

  2. 从节点(Slave/Replica)

    :默认「只读权限」(

    复制代码
    slave-read-only yes

    ),仅能执行读操作(

    复制代码
    GET

    复制代码
    HGET

    等),无法执行写操作,其核心作用是:

    • 分担主节点的读压力,提高查询性能;
    • 作为主节点的备份,当主节点故障时可快速切换,保障高可用;
    • 避免直接对主节点执行大量读操作,防止主节点过载。
  3. 只读模式的意义:从节点的只读模式是 Redis 的默认配置,目的是保障主从数据一致性。如果允许从节点写入,会导致从节点数据与主节点不一致,同步时出现冲突,最终引发整个架构的数据混乱。

本次异常的本质就是:Spring Boot 项目向一个处于「只读模式」的 Redis 从节点执行了写操作,被 Redis 按照默认规则拒绝,从而抛出 RedisReadOnlyException

更关键的是,本次场景中的从节点与主节点连接已断开(master_link_status:down),即使想同步数据也无法实现,此时的从节点只是一个「孤立的只读副本」,无法满足项目的读写需求。

三种解决方案(从临时应急到长期优化)

针对本次场景,我们提供三种解决方案,分别适用于「临时应急」「快速落地」「长期优化」三种场景,其中重点讲解「将从节点改造为独立主节点」的实战操作(也是本次现场操作的核心方案)。

方案一:临时应急 ------ 关闭从节点只读模式(快速解决写入问题)

该方案适用于「主节点无法快速修复,项目需要紧急启动」的场景,仅需关闭当前从节点的只读模式,即可让其支持写操作。

1. 核心操作命令

连接 Redis 从节点后,执行以下命令关闭只读模式:

运行

复制代码
# 关闭从节点只读模式(临时生效,Redis 重启后失效)
xxx.xx.121.134:6379> CONFIG SET slave-read-only no
OK
2. 验证效果

执行完命令后,可通过以下方式验证只读模式是否关闭:运行

复制代码
# 查看只读模式配置
xxx.xx.121.134:6379> CONFIG GET slave-read-only
1) "slave-read-only"
2) "0"  # 返回 0 表示只读模式已关闭(1 表示开启)

此时,再通过 Spring Boot 项目执行写操作,即可成功执行,异常消失。

3. 优缺点与注意事项
  • 优点:操作简单、快速生效,无需修改项目配置,无需搭建新节点;
  • 缺点:
    • 临时生效,Redis 重启后只读模式会恢复默认开启状态;
    • 从节点与主节点连接已断开,改造后的数据无法同步到原主节点,若后续恢复主从连接,可能引发数据冲突;
    • 无持久化配置的情况下,Redis 重启后数据会丢失;
  • 适用场景:紧急测试、项目临时启动、无需长期保留数据的场景;
  • 注意:生产环境严禁单独使用该方案,仅可作为应急过渡手段。

方案二:实战核心 ------ 将从节点改造为独立主节点(长期可用)

该方案是本次现场操作的核心,通过 SLAVEOF NO ONE 命令,将原本的「只读从节点」脱离主从架构,改造为「独立主节点」,具备完整的读写权限,且长期有效(即使 Redis 重启,也不会恢复为从节点)。

1. 核心操作步骤(全程实战可复现)
步骤 1:连接 Redis 从节点(同前文)

运行

复制代码
C:\Users\admin>redis-cli -h xxx.xx.121.134 -p 6379
步骤 2:脱离主从架构,升级为独立主节点

执行 SLAVEOF NO ONE 命令,让当前从节点与原主节点断开连接,成为独立主节点:

bash

复制代码
xxx.xx.121.134:6379> SLAVEOF NO ONE
OK
步骤 3:关闭只读模式,开启读写权限

bash

复制代码
xxx.xx.121.134:6379> CONFIG SET slave-read-only no
OK
步骤 4:验证改造结果

执行 INFO replication 命令,查看节点角色是否已变更:

bash

复制代码
xxx.xx.121.134:6379> INFO replication
# Replication
role:master  # 节点角色已从 slave 变为 master,说明升级成功
connected_slaves:0  # 无下级从节点,当前为独立主节点
master_failover_state:no-failover
master_replid:dc0281a4f7cfe69b0ad5459a5a540105eab3cb8e
master_replid2:c35f038e051be82e906b905e1dfa47b87df774c7
master_repl_offset:0
second_repl_offset:1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
步骤 5:验证读写功能(可选,确保改造有效)

运行

复制代码
# 执行写操作
xxx.xx.121.134:6379> SET test_key "spring_boot_redis_test"
OK

# 执行读操作
xxx.xx.121.134:6379> GET test_key
"spring_boot_redis_test"

# 删除测试数据(可选)
xxx.xx.121.134:6379> DEL test_key
(integer) 1
2. 额外问题处理:CONFIG REWRITE 报错

本次操作中,执行 CONFIG REWRITE 命令时出现报错:

bash 运行

复制代码
xxx.xx.121.134:6379> CONFIG REWRITE
(error) ERR The server is running without a config file
报错原因

CONFIG REWRITE 命令的作用是将当前的运行时配置写入 Redis 配置文件(redis.conf),实现配置持久化。本次报错是因为 Redis 启动时未指定配置文件,以「无配置文件模式」运行,无法写入配置。

解决方案
  1. 若无需持久化配置:可忽略该报错,当前的「主节点身份」和「关闭只读模式」的配置在 Redis 运行期间有效,仅需注意 Redis 重启后需重新执行命令(或指定配置文件启动);

  2. 若需要持久化配置:

    • 停止当前 Redis 进程:pkill redis-server

    • 创建或编辑

      复制代码
      redis.conf

      配置文件,添加以下配置:

      conf

      复制代码
      # 配置为独立主节点(无需连接其他主节点)
      # slaveof xxx.xx.232.83 25154 (注释掉原有主从配置)
      
      # 关闭只读模式,支持读写
      slave-read-only no
      
      # 其他必要配置(端口、绑定IP等)
      port 6379
      bind 0.0.0.0
      protected-mode no
    • 以配置文件启动 Redis:redis-server /usr/local/redis/conf/redis.conf(替换为你的配置文件路径);

    • 再次执行 CONFIG REWRITE,即可成功将配置写入文件。

3. 优缺点与注意事项
  • 优点:

    • 操作简单,无需搭建新节点,充分利用现有资源;
    • 改造后为独立主节点,支持完整读写权限,长期可用;
    • 无需修改 Spring Boot 项目配置,直接复用原有连接信息;
  • 缺点:

    • 原主节点的数据无法同步到当前节点(因原主从连接已断开),可能存在数据丢失;
    • 改造后的节点为单机模式,无高可用保障,若节点故障,项目将无法使用 Redis;
  • 适用场景:原主节点无法修复、项目需要长期使用 Redis、对高可用要求不高的场景(如中小型项目、测试环境);

  • 注意:生产环境中,改造后建议配置 Redis 持久化(RDB/AOF),防止数据丢失,同时可搭建新的主从架构,保障高可用。

方案三:长期优化 ------ 连接原主节点 / 搭建新主从架构(生产环境推荐)

该方案适用于「对数据一致性和高可用要求较高」的生产环境,核心思路是「回归标准主从架构」,避免单机节点的故障风险。

1. 子方案 3.1:修复并连接原主节点
  • 核心操作:

    1. 排查原主节点 xxx.xx.232.83:25154 的故障原因(如端口是否放行、Redis 是否启动、配置是否正确);

    2. 修复原主节点故障,确保其正常运行且支持读写操作;

    3. 修改 Spring Boot 项目

      复制代码
      application.yml

      配置,指向原主节点:

      yaml

      复制代码
      spring:
        redis:
          host: xxx.xx.232.83  # 原主节点 IP
          port: 25154           # 原主节点端口
          password: ''          # 原主节点密码(如有)
          timeout: 300s
          lettuce:
            pool:
              min-idle: 0
              max-idle: 8
              max-active: 8
              max-wait: -1ms
    4. 重启 Spring Boot 项目,验证读写功能正常。

  • 优点:保留原有数据架构,数据一致性有保障;

  • 缺点:排查和修复原主节点故障耗时较长。

2. 子方案 3.2:搭建新的 Redis 主从架构
  • 核心操作:

    1. 搭建新的 Redis 主节点(支持读写)和从节点(只读,分担读压力);
    2. 配置主从同步,确保从节点能正常同步主节点数据;
    3. 修改 Spring Boot 项目配置,指向新主节点,同时可集成 Redisson 等框架实现读写分离;
    4. 迁移原有数据到新主节点,确保数据不丢失。
  • 优点:高可用保障,读写分离提升性能,适合大规模生产环境;

  • 缺点:搭建和配置复杂,耗时较长,需要一定的运维经验。

3. 适用场景

生产环境、对数据一致性和高可用要求较高的项目、大规模分布式系统。

四、Spring Boot 项目验证与后续优化

1. 项目验证步骤

无论采用哪种方案,解决后都需要通过以下步骤验证项目是否正常:

  1. 启动 Spring Boot 项目 :观察日志,确保无 RedisReadOnlyExceptionRedisConnectionException 异常,相关 Bean(如 sysConfigServiceImpl)初始化成功;
  2. 验证写操作:触发项目中的 Redis 写操作(如更新系统配置、缓存用户信息);
  3. 验证读操作:查询已写入的缓存数据,确保能正常读取;
  4. 验证数据持久化(可选):重启 Redis 后,查看缓存数据是否存在,确保数据不会丢失。

2. 后续优化建议

  1. 配置 Redis 持久化:生产环境中,建议开启 RDB 和 AOF 混合持久化,既保障数据安全性,又兼顾性能;
  2. 设置 Redis 密码 :避免未授权访问,提高安全性,配置项 requirepass
  3. 限制连接 IP :通过 bind 配置项限制允许连接的 IP,避免恶意访问;
  4. 监控 Redis 状态 :使用 Redis 自带的 INFO 命令或第三方监控工具(如 Prometheus + Grafana),实时监控 Redis 的运行状态、内存使用、连接数等指标;
  5. 实现读写分离:生产环境中,通过 Redisson 等框架实现读写分离,写操作指向主节点,读操作指向从节点,提升项目性能。

五、总结

  1. RedisReadOnlyException 的核心原因是「向 Redis 从节点执行写操作」,从节点默认只读模式拒绝写请求;
  2. 排查问题的关键是使用 INFO replication 命令查看 Redis 节点角色,确认是否为从节点;
  3. 三种解决方案各有适用场景:临时应急可关闭只读模式,快速落地可将从节点改造为独立主节点,生产环境推荐搭建标准主从架构;
  4. SLAVEOF NO ONE 是将从节点改造为独立主节点的核心命令,配合 CONFIG SET slave-read-only no 可实现完整读写权限;
  5. 实现读写分离:生产环境中,通过 Redisson 等框架实现读写分离,写操作指向主节点,读操作指向从节点,提升项目性能。
相关推荐
成为你的宁宁3 小时前
【Zabbix 监控 Redis 实战教程(附图文教程):从 Zabbix-Server 部署、Agent2 安装配置到自带监控模板应用全流程】
数据库·redis·zabbix
墨雨晨曦885 小时前
如何保证redis和mysql数据一致性方案对比
数据库·redis·mysql
超级种码5 小时前
Redis:Redis 常见问题及解决思路
数据库·redis·缓存
结衣结衣.6 小时前
Redis中的string字符串介绍
数据库·redis·缓存
Python少年班6 小时前
MySQL MongoDB Redis官方本地百度网盘下载链接
redis·mysql·mongodb
Codeking__6 小时前
Redis分布式——分布式锁
数据库·redis·分布式
霸道流氓气质7 小时前
SpringBoot+modbus4j实现ModebusTCP通讯定时读取多个plc设备数并存储进redis中
java·spring boot·redis·modbustcp·plc
optimistic_chen7 小时前
【Redis系列】哨兵模式
linux·数据库·redis·分布式·哨兵
啊吧怪不啊吧7 小时前
极致性能的服务器Redis之Hash类型及相关指令介绍
大数据·数据库·redis·sql·mybatis·哈希算法