Redis 缓存策略与数据一致性
Redis 缓存策略与数据一致性
缓存能减少重复读取数据库的开销,但引入了数据副本,也带来了过期、并发和一致性问题。缓存策略需要和业务允许的陈旧时间、流量模式及故障处理方式一起设计。
1. Cache-Aside 读流程
最常见的旁路缓存流程是:
- 先读 Redis。
- 命中则返回缓存值。
- 未命中则读数据库。
- 把数据库结果写回 Redis,并设置过期时间。
伪代码:
1 | |
缓存键要包含业务对象和必要的隔离维度,例如 shop:detail:{shopId}。不同类型的数据需要设置合理的序列化格式和过期策略。
2. 更新时先写数据库,再删缓存
常见更新流程是先提交数据库更新,再删除对应缓存。后续读请求会从数据库取新值并重建缓存。直接更新缓存可能遇到并发写入覆盖、部分字段遗漏等问题,因此“更新数据库、删除缓存”通常更容易维护。
但这不是跨数据库和 Redis 的原子事务。数据库提交成功后,如果删除缓存失败,旧值可能继续存在到过期;删除成功后,另一个并发读也可能在数据库更新前读取旧值并重新写回。工程上可结合短 TTL、重试、消息补偿或版本机制降低风险。具体方案取决于可接受的陈旧窗口,不能只靠一句“先库后缓存”保证强一致。
3. 缓存穿透
穿透是查询大量不存在的数据,缓存持续未命中,数据库仍被访问。可选做法:
- 对确认不存在的结果缓存短时间的空值,注意区分“空值”和“缓存故障”。
- 使用布隆过滤器拦截明显不存在的键,但要接受误判并处理数据更新。
- 对接口做参数校验、限流和访问监控。
4. 热点过期与缓存击穿
热点键到期时,大量并发请求可能同时回源。可以用互斥锁让少数请求重建缓存,也可以使用逻辑过期:缓存值带一个逻辑过期时间,过期后先返回旧值并由后台刷新。互斥锁会增加等待和失败处理;逻辑过期会短暂提供旧数据,应只用于业务允许陈旧的场景。
5. 大量键同时过期与缓存雪崩
许多键在同一时刻失效,或 Redis 整体不可用,都可能让请求集中到数据库。设置 TTL 随机抖动、分批预热、保护数据库并发、限流降级和准备故障演练,都能降低冲击。缓存不能被当作数据库唯一副本。
6. 淘汰策略和可观测性
Redis 达到 maxmemory 后会根据淘汰策略处理键;策略要与键是否有 TTL、访问频率和服务用途匹配。上线后关注命中率、回源量、延迟、内存、淘汰数量和错误率,而不是只看 Redis 是否存活。
7. 小结
缓存的重点不在于给查询加一个 get 和 set,而在于明确陈旧容忍度、更新失败如何补偿、热点失效如何承压,以及 Redis 故障时数据库如何保护。先用最简单的 Cache-Aside,再根据监控数据补充复杂机制。
参考:Redis 缓存与淘汰策略文档。






