主备切换后客户端报错核心是未及时感知新拓扑,表现为readonly错误或连接超时;需检查客户端是否收到哨兵通知、dns缓存是否更新、连接池是否复用旧连接,并在写操作前执行role命令校验节点角色。

主备切换后客户端报错,核心不是“切没切成功”,而是“客户端是否及时跟上了新拓扑”。错误往往表现为 READONLY You can't write against a read only replica 或连接超时、i/o timeout,本质是客户端仍在往旧主(现为从)发写请求,或根本连不上新主。
查客户端是否收到切换通知
哨兵或集群的变更必须被客户端感知,否则它永远活在“旧世界”里:
- 搜应用日志关键词:Discovered new master(Lettuce)、Setting new master(JedisSentinelPool)。没出现说明事件监听失败
- JedisSentinelPool只传了一个哨兵地址?单点失效后无法拉取
__sentinel__:hello频道消息,整个发现链就断了 - Lettuce若用
RedisClient.connect(SENTINEL_URI)但ClientOptions中显式关闭了discovery,或未配置ClusterTopologyRefreshOptions,等于主动屏蔽刷新
查DNS和IP缓存是否锁死旧地址
即使哨兵返回了新主域名(如redis-master.default.svc.cluster.local),Java默认永久缓存DNS结果(sun.net.inetaddr.ttl = -1),连接池反复连一个已下线的IP:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 启动JVM加参数:
-Dsun.net.inetaddr.ttl=30 - 或代码中运行一次:
InetAddress.setCachePolicy(30)(更灵活,推荐) - K8s环境用headless Service?执行
kubectl get endpoints redis-master确认IP列表是否已更新
查连接池是否还在复用旧连接
ESTABLISHED连接可能还通着(PING成功),但节点角色已变。testOnBorrow=false或=true都拦不住READONLY错误:
-
testOnBorrow=true只发PING,不查ROLE;旧主变从后PING通、SET必败 - 真正要做的:每次写操作前,先执行
ROLE命令,检查返回值首项是否为"master" - 若返回
["slave", "192.168.1.10", "6379"],立刻丢弃当前连接,强制获取新连接 - 禁用
StatefulRedisConnection单例复用——它持有已失效的长连接引用
查跨语言/多服务共用Redis是否语义冲突
Java老系统和Go新系统同时连同一套Redis,主从切换后雪崩,常因序列化、Key命名、过期策略不一致导致缓存穿透:
- 对比两边
GET同一个Key的响应:是否一个返回空、一个返回乱码?说明序列化方式不同 - 检查Key前缀是否统一(如Java用
user:123,Go用U123),导致缓存完全不命中 - 查慢日志:
redis-cli slowlog get 10,确认是否有KEYS *类阻塞命令拖垮主节点,诱发被动切换










