缓存设计与一致性
1. 这是什么
缓存设计关注的是如何用 Redis 为系统减压,并在性能和一致性之间取得合理平衡。
它不是简单“把数据库结果放缓存里”这么直接。
一句话理解:
- 缓存是数据库前的一层性能缓冲区
- 它不是数据真相来源,而是性能和稳定性的权衡层
2. 为什么重要
缓存方案设计不好,系统不仅不会更快,还可能变得更难维护。
尤其在热点数据和高并发场景下,这些问题会非常突出:
- 缓存穿透
- 缓存击穿
- 缓存雪崩
- 数据不一致
3. 先建立直觉:缓存命中率高,不代表缓存设计就好
一个常见误区是:
- 只盯命中率
但缓存设计真正要同时考虑的是:
- 命中率
- 一致性
- 热点风险
- 失效策略
- 故障时的兜底路径
所以缓存不是“加上就好”,而是必须带着风险意识去设计。
4. 核心内容
4.1 缓存旁路模式
最常见的 Cache Aside 思路是:
- 先查缓存
- 没命中再查数据库
- 查到后回填缓存
- 更新时先改数据库,再删缓存
它不是唯一方案,但通常是学习和工程落地的起点。
4.2 缓存更新策略
常见设计包括:
- 更新数据库后删除缓存
- 更新数据库后同步更新缓存
- 延迟双删
- 通过消息或 binlog 异步刷新缓存
不同策略背后的本质问题都是:
- 一致性代价怎么控制
4.3 缓存穿透
缓存穿透指:
- 请求的数据根本不存在
- 缓存没有
- 数据库也没有
- 所有请求都穿透到数据库
常见应对:
- 缓存空值
- 布隆过滤器
- 参数校验
4.4 缓存击穿
缓存击穿通常指:
- 某个热点 key 失效
- 大量请求同时打到数据库
常见应对:
- 互斥重建
- 热点 key 永不过期或逻辑过期
- 提前刷新
4.5 缓存雪崩
缓存雪崩通常指:
- 大量 key 在同一时间失效
- 数据库被瞬时流量冲击
常见应对:
- TTL 加随机值
- 多级缓存
- 限流降级
- 预热热点数据
4.6 热点数据治理
热点问题本质上是:
- 单个 key 的访问被无限放大
常见处理:
- 本地缓存 + Redis
- 热点 key 分片
- 单独保护热点链路
- 热点数据预热
5. 学习重点
这一章最重要的是掌握:
- 缓存不是数据真相来源
- 穿透、击穿、雪崩是三种不同问题
- 一致性永远是缓存设计的重点问题
- 热点 key 需要专项治理
6. 常见问题
6.1 只考虑命中率,不考虑一致性
这样系统可能会很快,但会越来越不可信。
6.2 所有 key 同时过期
这是雪崩场景里最经典的错误之一。
6.3 热点 key 没有防护措施
热点问题往往不是平均流量能看出来的,而是在瞬时高峰暴露。
7. 动手验证
我已经验证过 Redis Docker 实验环境可用,你可以继续沿用:
bash
docker exec redis-lab redis-cli FLUSHALL7.1 观察 TTL 与失效
bash
docker exec redis-lab redis-cli SETEX cache:item:1 5 value-1
docker exec redis-lab redis-cli TTL cache:item:1这很适合体会:
- 缓存并不是永久有效
7.2 设计“随机 TTL”演练
把同类 key 的 TTL 不要都写成同一个值,而是设计成:
600 ~ 660秒随机
例如在代码层做:
java
int ttl = 600 + random.nextInt(60);7.3 穿透与空值缓存演练
bash
docker exec redis-lab redis-cli SETEX cache:user:404 60 NULL
docker exec redis-lab redis-cli GET cache:user:404你会看到:
- 即使数据库里没有这个用户,也可以用短 TTL 的空值挡掉反复穿透
8. 练习建议
- 设计一个商品详情缓存方案
- 模拟缓存击穿和雪崩场景
- 总结不同一致性策略的优缺点
- 为热点 key 设计专项保护方案
9. 自测问题
- 缓存穿透、击穿、雪崩的区别是什么?
- 为什么缓存一致性很难做到绝对完美?
- 热点 key 应该怎样治理?
- 为什么缓存命中率高不等于设计就一定合理?
10. 自测核对要点
- 穿透是查不存在数据,击穿是热点 key 失效,雪崩是大量 key 集中过期
- 一致性必须被设计出来,不能默认拥有
- 热点 key 会把单点压力放大成系统压力
- 缓存设计要同时考虑性能和稳定性