redis主从同步中del命令不被复制导致从库脏读,因后台异步淘汰不走复制流;需禁用lazyfree-lazy-eviction和lazyfree-lazy-expire,强制主线程删除以保证同步;读写分离下对强一致key应直读主库或配合wait命令。

Redis主从同步中DEL命令不被复制导致从库脏读
Redis默认配置下,DEL命令在主节点执行后,如果触发了内存淘汰(如maxmemory策略下的volatile-lru),部分键可能被后台线程异步删除——这种删除不会走AOF重写或复制缓冲区,也就不会同步到从节点。结果就是:主库已删,从库还存着旧值,读从库就拿到过期数据。
这不是bug,是Redis为性能做的取舍:异步淘汰不阻塞主线程,但牺牲了主从间删除操作的强一致性。
- 只有通过客户端显式执行的
DEL、EXPIRE、SET等命令才会进入复制流;后台淘汰产生的删除不会 - 启用
lazyfree-lazy-eviction yes(默认关闭)会加剧这个问题,因为淘汰完全交给后台线程 - 使用
noeviction策略可彻底规避,但要求你严格控制内存用量,否则写入直接失败
如何让主节点的淘汰动作也同步到从节点
核心思路只有一个:禁用异步淘汰路径,强制所有删除都走主线程+复制流程。这需要组合两个配置项:
- 设置
lazyfree-lazy-eviction no(默认值,但务必确认未被覆盖) - 设置
lazyfree-lazy-expire no(防止过期键异步删除) - 同时确保
maxmemory-policy不是noeviction时,淘汰行为仍由主线程触发(即不依赖后台线程)
验证是否生效:在主节点执行INFO memory,检查lazyfree_pending_objects长期为0;再人为触发淘汰(如CONFIG SET maxmemory 1mb后大量写入),观察从节点是否同步丢失对应key。
读写分离场景下避免脏读的兜底方案
即使配置正确,网络延迟、复制偏移滞后仍可能导致短暂不一致。生产环境不能只靠配置保底,得在应用层加约束:
- 对强一致性要求高的key(如订单状态、库存),读操作直接打主节点,不要走从库
- 若必须读从库,用
WAIT 1 1000命令确保当前写已传播到至少1个从节点(需开启repl-diskless-sync no且网络稳定) - 避免依赖TTL自动过期,改用业务层主动
DEL+ 同步等待,例如:DEL order:123; WAIT 1 500
注意:WAIT只保证传播,不保证从节点已执行——如果从节点正卡在慢查询或BGSAVE,它收到命令后仍会延迟执行。
为什么不用Redis 7的replica-serve-stale-data no?
这个配置常被误解为“禁止读脏数据”,实际作用只是:当从节点与主节点断连或复制偏移落后太多时,拒绝客户端连接请求。它**不解决已连接状态下因淘汰不同步导致的脏读**。
更关键的是,设为no会让从节点在主从断连期间直接返回错误,对读多写少的服务来说可用性风险远大于脏读风险——尤其在云环境网络抖动常见。
真正要防的不是“从库不可用”,而是“从库返回错数据”。前者能监控告警,后者静默发生,更难排查。










