什么是缓存击穿?该如何解决?
一、什么是缓存击穿
缓存击穿:一个热点key,在它过期的一瞬间,大量并发请求同时过来。 缓存查询结果为空,这些并发请求全部绕过缓存,冲到数据库查询这条数据。
和穿透、雪崩再区分一遍:
- 缓存穿透:查根本不存在的数据,缓存、DB都没有
- 缓存击穿:单个热点key失效,大量请求抢同一个key
- 缓存雪崩:一大批key同时失效 / Redis宕机,大规模流量打DB
举例子:秒杀爆款商品,每秒几千请求,缓存1小时过期。TTL时间到,Redis删掉这个key,一瞬间几千请求同时去数据库查这条商品数据,数据库压力瞬间飙升。
重点:是热点key,流量很大;只是这一个key失效,不是一批。
二、解决方案
方案1:互斥锁(分布式锁)
思路:热点key过期之后,只放行一个请求去查数据库、更新缓存;其他请求等待或者短暂重试,等缓存更新好之后再读缓存。 流程:
- 查询Redis,key不存在(过期)
- 尝试获取分布式锁(Redisson、Redis setnx)
- ✅ 获取到锁:查询DB,写入Redis,释放锁
- ❌ 没拿到锁:休眠一小段时间后重试查缓存,不直接查DB
✅ 优点:数据一致性好,不会读到旧数据;实现直观 ❌ 缺点:
- 有等待,并发高的时候会有请求阻塞,响应变慢
- 锁如果超时、死锁会有风险,要做好锁超时释放 👉 适合:对数据一致性要求高,能接受少量等待的热点数据
方案2:逻辑过期(热点key常用)
思路:不给key设置Redis的TTL,key物理上永不过期;过期时间放在value内部标记。 流程:
- 拿到缓存数据,对比里面的逻辑过期时间
- 没过期:直接返回数据
- 已经逻辑过期:尝试获取锁,抢到锁 → 开异步线程更新缓存;没抢到锁 → 直接返回旧数据
✅ 优点:用户不用等待,响应快;不会出现大量请求同时打DB ❌ 缺点:会短暂返回旧数据,不适合强一致性场景 👉 适合:商品详情、首页配置这类允许短暂不一致的热点,面试最爱问这个方案
方案3:热点key永不过期(简单粗暴)
思路:热点key不设置过期时间,由后台定时任务主动刷新缓存,不靠TTL自动过期。 定时任务每隔一段时间主动去DB拉取最新数据,更新Redis。
✅ 优点:简单,不会因为过期瞬间出现击穿 ❌ 缺点:
- 数据更新依赖定时任务,一致性差
- 如果DB数据更新了,要等下一次定时刷新才生效 👉 适合:变更频率很低的热点配置
三、生产推荐方案
- 要求强一致:分布式互斥锁
- 高并发热点、允许短暂旧数据:逻辑过期(优先)
- 变更极少的配置:定时主动刷新、不设过期时间
四、一句话总结
缓存击穿 = 单个热点key过期,并发蜂拥而至打数据库;要强一致用分布式锁,追求性能容忍旧数据就用逻辑过期。