三大缓存问题
缓存击穿、缓存雪崩、缓存穿透
| 问题 | 缓存击穿 | 缓存雪崩 | 缓存穿透 |
|---|---|---|---|
| 定义 | 某个热点 Key 过期瞬间,海量并发请求直接打到数据库,造成数据库压力暴涨 | 大量 key 在某个时刻同时过期,或 Redis 集群整体不可用,导致大量请求直接访问数据库,引起系统雪崩 | 客户端不断访问不存在的数据,由于缓存和数据库都没有该 key,所有请求都会访问 DB |
| 特点 | 单个热点key(缓存中不存在但db中存在) | 大量key | 不存在的key(缓存和db都不存在) |
| 影响 | 数据库压力突增 | 数据库可能宕机 | 数据库压力大,CPU高 |
缓存击穿方案
击穿是一个热点 key 过期,不是 Redis 挂了。目标是:同一时刻只有一条请求去打 DB,其余等它写回缓存。
| 方案 | 做法 | 注意 |
|---|---|---|
| 互斥锁 | 未命中时抢锁,抢到的查 DB 并回填,其余短等再读缓存 | 锁粒度必须是这个 key;等锁要有超时,避免死等 |
| singleflight | 进程内把同一 key 的并发合并成一次回源 | 多副本各自还会打一次 DB,集群要再加分布式锁 |
| 逻辑过期 | value 里带过期时间,过期后先返回旧值,异步刷新 | 短暂脏读,换来热点永不过期 |
| 预热 | 活动前把热点写进缓存,TTL 拉长并打散 | 只对已知热点有效 |
推荐默认:读路径 Cache Aside + 热点 key 用逻辑过期或 singleflight。不要在过期瞬间把所有流量放到 DB。
缓存雪崩解决方案
| 方案 | 说明 |
|---|---|
| 过期时间随机化 | 防止大量 key 同时过期 |
| Redis 高可用(哨兵/Cluster) | 防止 Redis 挂了导致雪崩 |
| 熔断/限流/降级 | 大量请求到 DB 时做保护 |
| 服务多副本 + 隔离池 | 防止压死核心服务 |
缓存穿透解决方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| 布隆过滤器 | 拦截不存在的 Key | 有误判,不可删除单个元素 |
| 缓存空值(短 TTL) | 简单、有效 | 缓存空间占用 |
| 参数校验 | 请求层过滤无效 ID | 覆盖有限 |
| 接口限流 | 减少恶意流量 | 不解决根本问题 |
预防措施
| 场景 | 预防措施 | 实现方式 |
|---|---|---|
| 击穿 | 预热机制 | 提前将热点数据加载到缓存 |
| 雪崩 | 监控预警 | 监控Redis性能指标 |
| 穿透 | 数据同步 | 定期同步布隆过滤器 |
如何实现数据的一致性
Cache Aside pattern
读流程:先查缓存→未命中缓存查DB→写缓存
写流程:先写DB→删除缓存
为什么写时是删缓存,而不是更新缓存?
避免 DB 和 Cache 不一致,同时删除逻辑简单可靠。
但还是存在并发问题:可能出现写后立即读,此时缓存未更新的瞬间,数据不一致。对于大部分业务来说,短期内不一致,最终一致性是可接受的。