跳到主要内容

缓存问题

问题

什么是缓存穿透、击穿、雪崩?分别怎么解决?

答案

一、三大缓存问题对比

问题定义原因严重程度
缓存穿透查询一个 不存在 的数据恶意攻击或代码 Bug⭐⭐⭐
缓存击穿一个热点 key 过期高并发请求同时穿透⭐⭐⭐⭐
缓存雪崩大量 key 同时过期集中过期或 Redis 宕机⭐⭐⭐⭐⭐

二、缓存穿透

场景:请求 id = -1 的数据,缓存没有,数据库也没有。每次请求都直接打到数据库。

解决方案

方案 1:缓存空值

查数据库 → 不存在 → 缓存 key → null(短过期时间,如 2 分钟)
优点缺点
简单大量不存在的 key 浪费内存

方案 2:布隆过滤器(推荐)

布隆过滤器用位数组 + 多个哈希函数判断元素是否存在:

  • 不存在 → 一定不存在
  • 存在 → 可能存在(有一定误判率)

三、缓存击穿

场景:某个热点 key(如热门商品)过期的一瞬间,大量请求同时到来,全部打到数据库。

解决方案

方案 1:互斥锁

缓存未命中 → 获取分布式锁 → 只有一个请求查数据库 → 写入缓存 → 释放锁
其他请求 → 等待 → 从缓存读取

方案 2:逻辑过期

不设置实际过期时间,在 value 中存一个逻辑过期时间。
读取时判断:
- 未过期 → 直接返回
- 已过期 → 返回旧数据 + 异步线程去更新缓存
方案一致性可用性
互斥锁强一致部分请求需等待
逻辑过期短暂不一致高可用,不阻塞

方案 3:热点 key 永不过期

对已知的热点 key,不设过期时间,在数据变更时主动更新缓存。

四、缓存雪崩

场景:大量 key 在同一时间过期,或者 Redis 实例宕机,导致大量请求涌向数据库。

解决方案

场景 1:大量 key 同时过期

# 添加随机过期时间
expireTime = baseExpire + random(0, 300) # 基础过期 + 0~300 秒随机

场景 2:Redis 宕机

方案说明
Redis 高可用Sentinel / Cluster
本地缓存(L1)Caffeine 等进程内缓存
限流降级保护数据库不被打崩
熔断器数据库承受不住时直接返回降级响应

五、综合防御体系


常见面试问题

Q1: 缓存穿透和缓存击穿的区别?

答案

  • 穿透:查的数据 不存在(数据库也没有)
  • 击穿:查的数据 存在但缓存过期了(数据库有)

两者都会导致请求打到数据库,但原因和解决方案不同。

Q2: 布隆过滤器的误判怎么处理?

答案
误判意味着布隆过滤器说「存在」但实际「不存在」。这种情况请求会穿透到数据库查一次,发现不存在后可以缓存空值。误判率可通过增大位数组或增加哈希函数来降低(通常控制在 1% 以内)。

Q3: 热点 key 如何发现?

答案

  1. 业务预判:秒杀商品、热门话题等可预知的热点
  2. Redis 监控redis-cli --hotkeys(4.0+ LFU 模式)
  3. proxy 统计:中间件层统计 key 的访问频率
  4. 本地缓存兜底:对已知热点 key 加本地缓存

相关链接