Redis从库默认只读但可被绕过,需服务端配置、客户端连接隔离与应用层逻辑三重保障;Lettuce需用ReadFrom.SLAVE_PREFERRED并避免写调用,redis-py应专用从库实例且禁用写操作。

Redis从库默认是只读的,但客户端可能绕过这个限制
Redis 从库(slave)在服务端层面默认开启 readonly 模式(即配置项 slave-read-only yes),这意味着它会拒绝所有写命令(如 SET、INCR、DEL 等),返回 READONLY You can't write against a read only slave. 错误。但这个保护仅作用于服务端解析命令时——如果客户端在连接后主动发送 READWRITE 命令,且从库配置了 slave-read-only no(极不推荐),或使用某些低层驱动未校验角色就直接发写请求,仍可能触发意外写入。
因此,**不能依赖服务端 readonly 配置作为唯一防线**;客户端必须明确按只读意图建立连接,并避免任何写操作逻辑混入读路径。
Java客户端(Lettuce)如何强制走只读连接
Lettuce 的 RedisClient 默认连接主节点,要连从库并确保只读,关键在 ReadFrom 配置和连接方式:
- 用
MasterSlaveTopologyProvider或StaticMasterSlaveTopologyProvider自动发现从库,再通过ClientResources.builder().topologyRefreshOptions(...)启用定期拓扑刷新 - 创建
StatefulRedisMasterSlaveConnection时,显式指定ReadFrom.SLAVE_PREFERRED(优先从从库读,故障时自动切主)或ReadFrom.SLAVE(严格只连从库,主库不可用则报错) - 务必禁用
setReadOnly(true)以外的写操作:Lettuce 不会拦截你调用connection.sync().set(...),即使连的是从库——错误会在执行时由 Redis 返回READONLY - 若使用 Spring Data Redis,需配
RedisProperties.Pool+RedisSentinelConfiguration或RedisClusterConfiguration,并在RedisTemplate上设置setEnableTransactionSupport(false)(事务在从库无意义)
RedisClient client = RedisClient.create("redis://10.0.1.10:6379");
StatefulRedisMasterSlaveConnection<string string> connection =
MasterSlave.connect(client, new Utf8StringCodec(), RedisURI.create("redis://10.0.1.20:6379"));
connection.setReadFrom(ReadFrom.SLAVE_PREFERRED);
</string>
Python客户端(redis-py)连接从库后怎么防写
redis-py 本身不内置主从路由逻辑,需手动管理连接对象。常见误区是:用 redis.Redis(host="slave-ip", port=6379) 连上从库,就以为安全了——其实只要没做运行时约束,代码里仍可调 r.set(...),等执行才报错。
- 不要复用同一个
Redis实例做读写混合操作;为读专门建一个只连从库的实例,命名如redis_slave - 在初始化时加一层封装,例如重写
__getattr__拦截写命令(不推荐,影响性能且易漏),更稳妥的是靠团队规范 + Code Review + 单元测试断言「只读连接不调用写方法」 - 配合 Redis 配置:确认从库
slave-read-only yes(默认值),并检查是否被运维脚本覆盖;同时禁用CONFIG SET slave-read-only no权限(通过rename-command CONFIG "") - 若用
redis-py-cluster,注意它不支持纯从库读取模式,需降级到单实例连接或换用redis-py手动分发
为什么不能靠网络层隔离来“保证”只读
有人想用防火墙或代理(如 Twemproxy、Codis)屏蔽从库的写命令,这看似可靠,实则危险:
- Redis 协议是纯文本(RESP),代理很难精准识别语义(比如
EVAL脚本里混着redis.call("SET",...)) - 写命令不一定带明显关键字:
EXPIRE、PEXPIREAT、MOVE都算写操作,但容易被规则漏掉 - 代理引入额外延迟和单点故障,且无法防止客户端直连从库 IP 绕过代理
- 最致命的是:它掩盖了代码层的职责混乱——读写逻辑耦合、连接复用、测试缺失等问题依然存在,只是延后暴露
真正的只读保障来自三层合力:服务端 readonly 配置兜底 + 客户端连接与调用路径隔离 + 应用层逻辑清晰划分读写上下文。其中客户端配置是可控性最强的一环,也是最容易被当成“已配置好”而忽略验证的一环。











