缓存穿透、缓存击穿、缓存雪崩
缓存处理流程
前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果
缓存穿透
查询的数据在数据库中并不存在,缓存中没有,仍然去数据库中查询,从而可能压跨数据库。
如果有人恶意攻击,查询没有的数据,缓存将相当于失效了。
数据库没有,缓存没有
解决办法:
- 使用布隆过滤器(BloomFilter)或者压缩 filter 提前拦截
- 将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。这种情况我们一般会将空对象设置一个较短的过期时间
- 拉黑该 IP 地址
- 对参数进行校验,不合法参数进行拦截
缓存击穿
数据在数据库中存在,但在 redis 中的某个时间节点过期了,此时刚好有大量请求发送过来,发现此时缓存过期,都会区数据库查询,这时候大并发的请求可能会把数据库压垮。
解决办法:
- 热点数据设置永不过期
- 加上互斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到 redis 缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存
缓存雪崩
高并发的情况下,大量缓存失效或者缓存层出现故障,redis服务出现故障,于是所有请求都会达到数据库,数据库调用量暴增,造成数据库会挂掉的情况
解决方法:
-
随机设置 key 失效时间,避免大量 key 集体失效。
setRedis(Key,value,time + Math.random() * 10000); -
若是集群部署,可将热点数据均匀分布在不同的 Redis 库中也能够避免 key全部失效问题
-
不设置过期时间
-
跑定时任务,在缓存失效前刷进新的缓存
总结
雪崩是大面积的key缓存失效;
穿透是redis里不存在这个缓存key;
击穿是redis某一个热点 key 突然失效,
最终的受害者都是数据库
对于“Redis 宕机,请求全部走数据库”这种情况,我们可以有以下的思路:
事发前:实现 Redis 的高可用(主从架构+Sentinel(哨兵),尽量避免 Redis
挂掉这种情况发生。
事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)一Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后:redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据