什么是缓存击穿?该如何解决?

摘要

什么是缓存击穿?该如何解决? 一、什么是缓存击穿 缓存击穿:一个热点key,在它过期的一瞬间,大量并发请求同时过来。 缓存查询结果为空,这些并发请求全部绕过缓存,冲到数据库查询这条数据。 和穿透、雪崩再区分一遍: 缓存穿透:查根本不存在的数...

什么是缓存击穿?该如何解决?

一、什么是缓存击穿

缓存击穿:一个热点key,在它过期的一瞬间,大量并发请求同时过来。 缓存查询结果为空,这些并发请求全部绕过缓存,冲到数据库查询这条数据。

和穿透、雪崩再区分一遍:

  • 缓存穿透:查根本不存在的数据,缓存、DB都没有
  • 缓存击穿:单个热点key失效,大量请求抢同一个key
  • 缓存雪崩:一大批key同时失效 / Redis宕机,大规模流量打DB

举例子:秒杀爆款商品,每秒几千请求,缓存1小时过期。TTL时间到,Redis删掉这个key,一瞬间几千请求同时去数据库查这条商品数据,数据库压力瞬间飙升。

重点:是热点key,流量很大;只是这一个key失效,不是一批。

二、解决方案

方案1:互斥锁(分布式锁)

思路:热点key过期之后,只放行一个请求去查数据库、更新缓存;其他请求等待或者短暂重试,等缓存更新好之后再读缓存。 流程:

  1. 查询Redis,key不存在(过期)
  2. 尝试获取分布式锁(Redisson、Redis setnx)
  3. ✅ 获取到锁:查询DB,写入Redis,释放锁
  4. ❌ 没拿到锁:休眠一小段时间后重试查缓存,不直接查DB

✅ 优点:数据一致性好,不会读到旧数据;实现直观 ❌ 缺点:

  • 有等待,并发高的时候会有请求阻塞,响应变慢
  • 锁如果超时、死锁会有风险,要做好锁超时释放 👉 适合:对数据一致性要求高,能接受少量等待的热点数据

方案2:逻辑过期(热点key常用)

思路:不给key设置Redis的TTL,key物理上永不过期;过期时间放在value内部标记。 流程:

  1. 拿到缓存数据,对比里面的逻辑过期时间
  2. 没过期:直接返回数据
  3. 已经逻辑过期:尝试获取锁,抢到锁 → 开异步线程更新缓存;没抢到锁 → 直接返回旧数据

✅ 优点:用户不用等待,响应快;不会出现大量请求同时打DB ❌ 缺点:会短暂返回旧数据,不适合强一致性场景 👉 适合:商品详情、首页配置这类允许短暂不一致的热点,面试最爱问这个方案

方案3:热点key永不过期(简单粗暴)

思路:热点key不设置过期时间,由后台定时任务主动刷新缓存,不靠TTL自动过期。 定时任务每隔一段时间主动去DB拉取最新数据,更新Redis。

✅ 优点:简单,不会因为过期瞬间出现击穿 ❌ 缺点:

  • 数据更新依赖定时任务,一致性差
  • 如果DB数据更新了,要等下一次定时刷新才生效 👉 适合:变更频率很低的热点配置

三、生产推荐方案

  • 要求强一致:分布式互斥锁
  • 高并发热点、允许短暂旧数据:逻辑过期(优先)
  • 变更极少的配置:定时主动刷新、不设过期时间

四、一句话总结

缓存击穿 = 单个热点key过期,并发蜂拥而至打数据库;要强一致用分布式锁,追求性能容忍旧数据就用逻辑过期。