缓存读得到,更新缓存时却报 READONLY You can't write against a read only replica。如果之前一直正常,先记下第一次报错的时间,看看那段时间 Redis 有没有切换主节点。也可能只有一台应用实例报错,那就从那台实例用的连接开始查。

下面按 Redis 6/7 的主从复制和 Sentinel 部署说明,Spring Boot 示例使用 3.5.x、JDK 17 及以上、默认 Lettuce 自动配置。Redis Cluster 和云服务也可能遇到类似报错,接入方式分别看后文。

先查报错的连接落到了哪台 Redis

从异常链确认是哪条 Redis 命令被拒绝,记下应用实例、Redis 连接工厂和当时的目标地址。报错里的 READONLY 表示 Redis 拒绝了写入类命令。像续期用的 EXPIRE 也会改变数据,排查时可以沿调用栈往下找。若客户端经过 DNS、负载均衡或代理,诊断命令查到的是这一次连接落到的节点。要定位那次失败的连接,还得对照客户端连接日志或代理的后端记录。Redis READONLY 错误说明

在同一网络环境用有诊断权限的账号连接已确认的 Redis 数据节点。下面用 Redis 7 的 redis-cli 举例。把域名、端口和 ACL 用户换成实际值,--askpass 会交互询问密码。TLS 实例需要按服务配置传入 --tls 和受信 CA;无密码的本地测试实例可省去认证选项。托管实例若不开放这些命令,使用平台的节点角色和连接信息。Redis CLI 文档

redis-cli -h redis-a.example.internal -p 6379 --user diagnostic --askpass

连进去以后,在同一个会话执行以下命令。

ROLE
INFO replication

ROLE 第一项为 master 时,说明这台实例在采样时自报主节点。显示 slave 时,它是副本。继续看 INFO replication 里的 master_host、master_port 和 master_link_status,记录它在跟随谁。若只查到 master,再对照报错时间、节点切换记录和旧连接的地址,避免把现在的角色当成报错那一刻的角色。ROLE 命令 · INFO replication 字段

如果日志里看到的是连接超时、命令超时或 NOPERM,分别确认网络、响应等待和 ACL。连接超时可以顺着Spring Boot Redis 连接超时的排查步骤定位。

有 Sentinel 时,核对它发现的主节点

一个常见场景是应用一直连 A 的固定地址。切换后 B 已经成了主节点,A 恢复后成了副本。原来访问 A 的写入开始报错,部分读取还能返回。

从应用所在网络连到已配置的 Sentinel 节点,端口用该环境的 Sentinel 端口,认证用 Sentinel 自己的账号配置。把 cache-main 换成实际监控名称,再查一次当前主节点。

SENTINEL get-master-addr-by-name cache-main

返回的是主节点的地址和端口。用上一节的方法查这个地址的 ROLE,并检查应用是否能访问该地址。空返回时核对名称与 Sentinel 配置。几个 Sentinel 给的地址还没对齐时,结合切换记录判断当时的状态,再核对客户端是否完成主节点发现与重连。容器或跨网络部署还要留意 Sentinel 通告的地址是否在应用网络内可达。Redis Sentinel 客户端规范 · Redis Sentinel 部署文档

按实际部署修连接

固定主从。 检查应用实际生效的写入地址。比如某个实例还带着旧环境变量,或者配置中心把写入口指到了读地址。把写入工厂指到确认过的主节点入口,再让受影响实例重新建立连接。

Sentinel。 使用支持 Sentinel 的连接工厂,让客户端按监控名称发现主节点。下面是 Spring Boot 3.5.x 的示例,假设应用使用 Starter 自动创建连接工厂,并且已有三个 Sentinel 入口。

spring:
  data:
    redis:
      sentinel:
        master: cache-main
        nodes:
          - sentinel-1.example.internal:26379
          - sentinel-2.example.internal:26379
          - sentinel-3.example.internal:26379

有 ACL 或 TLS 的环境,沿用实际的 Redis 数据节点认证、Sentinel 认证和 TLS 设置。先看报错的 RedisTemplate 绑定哪一个 RedisConnectionFactory。项目如果自己创建了工厂,或用了独立配置的 Redisson,改上面这段 YAML 后还需要核对实际连接来源。切换过后继续报错时,检查客户端收到的地址和它新建连接的目标,别只用一条新的命令行连接验证。Spring Boot 3.5 Redis 配置项

Redis Cluster 或托管服务。 Cluster 使用支持集群拓扑的客户端,核对失败命令的键所在槽和实际写入节点。托管服务按平台提供的写入口接入,检查有没有把读端点填进写入工厂。副本继续保留只读,修复点在应用写入所走的连接。Redis Cluster 的 READONLY 命令说明 · Redis 副本只读说明

用应用的写入路径复测

用原来报错的应用实例和连接工厂复测。可以在测试环境用业务允许的临时前缀写一个带短 TTL 的唯一键,确认写入成功,再从写入路径对应的主节点读回。同一主节点读写通过后,再检查正常读入口。副本读取可能有复制延迟,以写入成功的返回和主节点读回作为这次写入的证据。

如果故障发生在切换期间,挑一条实际失败的业务请求对照结果。一次业务调用可能发过多条命令,或者已经修改了数据库。重试前先查幂等键或业务结果,尤其是计数、入队这类重复执行会改变结果的操作。修好后关注的是原实例的写入错误是否停止、新建连接是否落在正确入口,以及实际业务请求是否恢复。