必须开replica-read-only yes,它是协议层拦截写命令的保险栓;Redis 6.0+默认启用,但老版本或手动配置常设为no,导致从库可写引发数据不一致,需CONFIG SET即时生效并持久化到redis.conf。

必须开 replica-read-only yes,且不能只靠它拦住所有写入——服务端配置、客户端连接方式、应用逻辑三者缺一不可。
确认并强制开启 replica-read-only
这个配置不是“让从库只读”,而是协议层拦截写命令的保险栓。Redis 6.0+ 默认是 yes,但老版本或手动改过 redis.conf 的实例常被设为 no,导致从库真能接收 SET、DEL 等写命令:
- 连上从库执行
CONFIG GET replica-read-only,返回["replica-read-only","no"]就得立刻修 - 运行时修复:
CONFIG SET replica-read-only yes(立即生效) - 但重启会回退,必须同步更新
redis.conf文件,确保有明确一行:replica-read-only yes - Redis 5.0+ 兼容旧名
slave-read-only,但新参数名更准确,建议统一用replica-read-only
绕过 replica-read-only 的真实路径有哪些
很多人以为设了 yes 就万事大吉,其实以下场景仍可能“写成功”或造成等效脏写:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 本地连接(
redis-cli -h 127.0.0.1)在某些 Redis 版本 + ACL 配置下可能绕过限制 -
EVAL脚本:即使replica-read-only yes,脚本里调用redis.call('SET', ...)会在运行时报错,但GET+SET组合逻辑可能已读到中间态 - ACL 权限覆盖:如果用户有
+@all或显式+set权限,会优先于replica-read-only生效 - 运维误操作:手动
CONFIG SET replica-read-only no调试后忘记恢复,又没进配置中心流程,比配置本身更难追踪
客户端连接层必须隔离读写路径
服务端只拦命令,不拦连接。客户端若把从库当主库用,照样发 SET,错误只在执行时抛出,此时业务逻辑可能已崩:
- Lettuce 必须用
ReadFrom.SLAVE_PREFERRED或ReadFrom.SLAVE,且禁止在该连接上调用sync().set()类方法 - redis-py 不自动路由,需严格区分两个连接实例:
redis.Redis(host="master")专用于写,redis.Redis(host="slave")实例禁用所有写方法封装 - Spring Data Redis 中,
RedisTemplate若配了哨兵或集群,要确认setEnableTransactionSupport(false),事务在从库无意义且易掩盖问题
生产环境真正兜底的三件事
光设对配置远远不够。最容易被忽略的是监控盲区和临时改动:
- 定期用脚本检查所有从库的
CONFIG GET replica-read-only和INFO replication | grep role,值不是yes或角色不是slave就告警 - 禁止应用直连从库做任何写逻辑——这不是靠
replica-read-only拦得住的,得在服务发现层(如 Nacos、Consul)或 SDK 层强制读写分离路由 - 对强一致 key(如库存、订单状态),不要依赖从库读,哪怕加了
WAIT;WAIT只保证传播,不保证执行,从库卡在BGSAVE或慢查询时,DEL仍在缓冲区排队
最危险的不是配置漏设,而是某次调试中 CONFIG SET replica-read-only no 后忘了恢复,又没走配置管理流程——这种改动不会出现在 git diff 里,也不会被配置中心捕获,只能靠监控和连接层硬隔离来兜底。

















