readonly错误根本原因是写请求被路由到只读从节点,而非同步延迟或客户端响应慢;本质是架构配置缺陷,如未正确配置读写分离、连接地址指向从库、哨兵/集群拓扑未及时刷新等。

READONLY错误根本不是客户端“延迟”,而是路由错发
出现 READONLY You can't write against a read only replica 时,不是客户端慢了、没反应过来,而是它**把写命令发到了从节点上**——这个动作在连接建立那一刻就已确定,不存在“延迟生效”一说。所谓“路由延迟”,其实是误读:真实问题是客户端压根没配置读写分离策略,或策略未生效,导致所有命令(包括 SET、INCR)都走同一条连接,而这条连接连的恰好是个只读从节点。
Lettuce 的 ReadFrom.REPLICA_PREFERRED 不会自动隔离读写路径
很多人以为只要设置了 ReadFrom.REPLICA_PREFERRED,Lettuce 就会把读打到从节点、写打到主节点——这是错的。Lettuce 默认仍把所有命令(含写)发往当前连接的节点;ReadFrom 只影响 读命令的节点选择逻辑,不改变写命令的行为。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 写操作永远只走主节点,但前提是客户端得先连上主节点——如果初始化时
RedisURI指向的是从节点 IP,那写命令上来就报错 -
ReadFrom.REPLICA_PREFERRED的作用是:当执行GET、HGETALL等读命令时,Lettuce 自动从可用 replica 中选一个健康节点转发,而不是固定打主节点 - 它不拦截、不重写、不校验命令类型;你代码里调
connection.sync().set("k","v"),它照样发过去,只是目标节点如果是从节点,Redis 内核直接拒绝
Jedis 和 redis-py 完全不处理读写分离,靠人兜底
Jedis 4.x 之前没有 ReadMode 概念,JedisSentinelPool 默认只返回主节点连接;redis-py 更彻底:redis.Redis(host="10.0.2.5", port=6379) 连上去就是连上去,它不管你是主是从,也不拦你调 r.set(),直到 Redis 返回 READONLY 才抛异常。
- Jedis 4.x+ 需显式传
ReadMode.SLAVE给JedisFactory,且仅对JedisSentinelPool有效;用JedisCluster则必须自己实现ClusterCommand拦截读命令 - redis-py 没有内置路由能力,常见做法是维护两套连接池:
master_client和slave_client,业务层手动分发命令——但极易漏判、写错变量名 - 所有手动管理方式都面临一个问题:主从切换后,旧连接池里的连接可能已失效,而新主节点地址不会自动刷新到客户端
哨兵/集群模式下,客户端感知失败转移需要时间
即使你用了 Lettuce + 哨兵,READONLY 仍可能在故障切换后持续几秒——这不是客户端“延迟”,而是它正在重新发现拓扑。
- Lettuce 默认每 10 秒轮询一次哨兵获取主节点地址,期间若旧主已降级为从,新写请求就会打到它身上,触发错误
- 可通过
ClientOptions.builder().pingBeforeActivateConnection(true)+timeout(500, TimeUnit.MILLISECONDS)缩短无效连接存活时间 - 更关键的是:确保哨兵配置中
quorum合理、down-after-milliseconds不设过长(跨机房建议 ≤ 3000),否则客户端要等更久才确认主节点变更










