# 1、缓存穿透
# 1、定义
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会被打到数据库上。

# 2、产生原因
- 自身业务代码问题
- 恶意攻击,爬虫造成空命中
# 3、常见的两种解决方案
# 缓存空对象
优点:实现简单,维护方便缺点:① 额外的内存消耗② 可能造成短期的不一致
# 布隆过滤器
优点:内存占用较少,没有多余 Key 缺点:① 实现复杂② 存在误判可能
补充:布隆过滤器,当再布隆过滤器中获取数据说是不存在时,那一定是不存在的,但是如果说是存在的话,数据实际上说不定是不存在的。所以使用布隆过滤器也可能会出现缓存穿透问题。
# 增强 id 的复杂度避免被猜测 id 规律
# 做好数据的基础格式校验
# 加强用户权限校验
# 做好热点参数的限流
# 2、缓存雪崩
# 1、定义
缓存雪崩是指在同一时段大量的缓存 Key 同时失效或者 Redis 服务宕机,导致大量请求到达数据库,带来巨大压力。

# 2、解决方案
- 给不同的 Key 的 TTL 添加随机值
- 利用 Redis 集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
# 3、缓存击穿
# 1、定义
缓存击穿问题也叫做热点 Key 问题,就是一个高并发访问并且缓存重建业务比较复杂的 Key 突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
如上图所示的,线程 1 查询缓存但是缓存并没有命中,就开始查询数据库了,然后线程 2、3、4 也陆续查询了缓存没有命中,都去查询数据库并重建缓存。导致 db 的压力猛增,造成 dp 有可能会宕机。
# 2、解决方案
# ① 互斥锁
如上图所示:线程 1 和线程 2,线程 1 先查询缓存,但是没有命中,然后线程 1 获取互斥锁,后进行查询数据库重建数据缓存,期间线程 2 查询缓存,同样没有命中,接着线程 2 获取互斥锁,但是并没有获取到锁,所以线程 2 就在等待并重试,直到线程 1 重建数据缓存并写入缓存结束后释放互斥锁,然后线程 2 获取到互斥锁缓存命中获取到数据就可以返回了。
# ② 逻辑过期
上图为逻辑过期的处理流程:线程 1 查询缓存,发现逻辑过期时间已经过期了,后获取互斥锁,然后开启新的线程:线程 2,而线程 1 直接拿旧的数据返回了,线程 2 重建数据并设置逻辑过期时间,在此时间之内线程 3 查询缓存发现逻辑过期时间过期了,并且获取互斥锁失败,那么就证明有线程在重建数据了,线程 3 就拿旧的数据返回。这时线程 2 重构数据成功,并释放了互斥锁。线程 4 过来直接直接命中缓存并且没有逻辑过期,就返回了。
# 总结:
两种方式各有优缺点:
| 解决方案 | 优点 | 缺点 |
|---|---|---|
| 互斥锁 | 没有额外的内存消耗保证一致性实现简单 | 线程需要等待,性能受影响可能有死锁风险 |
| 逻辑过期 | 线程无需等待,性能较好 | 不保证一致性有额外内存消耗实现复杂 |
建议再看一篇这个文章,写的比我好多了:十分钟彻底掌握缓存击穿、缓存穿透、缓存雪崩 - 三分恶 - 博客园