缓存的价值是用更快、更便宜的读取换取有限时间的不一致风险。设计缓存前要先回答:缓存什么、允许旧多久、何时删除、失败时能否回源,以及热点失效时谁来重建。

Cache Aside 基本流程

读取:

1
2
3
读取 Redis
-> 命中:直接返回
-> 未命中:查询数据库 -> 写入 Redis -> 返回

更新常用做法:

1
更新数据库成功 -> 删除缓存

删除而不是直接更新缓存,可以减少多处业务同时维护复杂缓存结构的成本。下一次读取会从数据库重建。

为什么仍不是绝对一致

考虑这个竞态:

1
2
3
请求 A 缓存未命中,读到数据库旧值
请求 B 更新数据库,并删除缓存
请求 A 把刚才的旧值写回缓存

所以“更新数据库再删缓存”只能降低不一致窗口。可结合短 TTL、版本号、延迟双删、消息失效通知或串行更新处理;强一致要求高的字段不应简单依赖旁路缓存。

穿透、击穿和雪崩

问题 发生方式 常见处理
穿透 持续查询根本不存在的数据 参数校验、空值短缓存、布隆过滤器
击穿 单个热点 Key 失效,大量请求同时回源 互斥重建、逻辑过期、热点预热
雪崩 大批 Key 同时失效或 Redis 整体不可用 TTL 抖动、多级缓存、限流降级、高可用

空值缓存要设置较短 TTL,避免真实数据创建后仍长时间返回“不存在”。布隆过滤器可能误判存在,因此不能替代数据库校验。

热点 Key 的互斥重建

1
2
3
4
缓存未命中
-> 尝试 SET lock token NX PX 3000
-> 获得锁:查数据库、写缓存、按 token 释放锁
-> 未获锁:短暂等待后再次读取缓存

释放锁时必须比较 token,避免误删已经被其他请求重新获得的锁。锁也要有过期时间,并为数据库失败准备降级路径。

不要缓存什么

  • 访问很少且变化频繁的数据。
  • 无法定义失效规则的关键状态。
  • 超大对象或无边界列表。
  • 高度敏感且缺少隔离措施的数据。
  • 本来一次索引查询就足够快的数据。

监控指标

至少观察命中率、延迟、连接数、内存、驱逐数量、过期数量、热 Key、回源 QPS 和数据库耗时。命中率下降只是现象,还要判断是业务流量变化、TTL 设置、Key 设计还是 Redis 故障。

延伸阅读

站内搜索

没有找到内容!