缓存雪崩、缓存穿透、缓存击穿是什么?如何解决?
我们要使用缓存的时候,不可避免地要面对缓存雪崩、缓存穿透、缓存穿透的问题。如果做缓存没有考虑到这些问题,那是不合格的系统。推而广之可以说,引入任何一项技术,只考虑它的优点而没有考虑到它带来的弊端,那就是在挖坑。
缓存雪崩
我们现在有一个系统。高峰期每秒有5000个请求发到系统 A 上。系统A用缓存抗住4000个请求,其中有1000个请求会打到 MySQL 上。
突然缓存挂掉了,这就导致原本缓存抗的4000个请求一下子都打在数据库上。如果我们用的是 MySQL 数据库,每秒5000个请求过来,MySQL 一下子就挂了。接着 DBA 重启 MySQL,还是 5000 个请求一下子打过来,MySQL 又挂了。数据库一挂,整个系统就挂了。这就是缓存雪崩。
缓存雪崩指的是由于缓存断掉之后,引起一些列的连锁反应,就像雪崩一样,一批服务都挂掉了。
缓存雪崩要怎么解决?
- 事前:redis 做好高可用,主从架构、redis cluster,避免全线奔溃
- 事中:本地 ehcache 存储 + hystrix 限流&降级,避免 MySQL 被打死
- 事后:redis 持久化,重启之后立即磁盘加载数据,快速恢复缓存
缓存穿透
现在任然是高峰期每秒5000次请求,其中有4000次是黑客发过来。比如说我们缓存的是商品订单的id,现在黑客发过来的4000个请求id都为负数。
我们是没有负数的id的,所以请求过来无法命中缓存,这样一来黑客的4000个请求就完全穿透缓存,直接打到了 MySQL 上,MySQL 就这样挂了。
解决方案:
- 缓存空对象:缓存中没有查到就存一个空值到缓存中,比如
set -999 UNKNOW
。 - 布隆过滤器:提前生成一个布隆过滤器,无法通过过滤器的直接返回空。
缓存击穿
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。
方案 | 优点 | 缺点 |
---|---|---|
互斥锁 | 1. 思路简单 2. 保证一致性 |
1. 代码复杂度增加 2. 存在死锁风险 |
永远不过期 | 基本杜绝热点key重建问题 | 1. 不保证一致性 2. 逻辑时间增加维护成本和内存成本 |