12、缓存穿透、缓存击穿、缓存雪崩
一、缓存功能
当系统处于高并发时,为了防止数据库压力过大,引入缓存处理功能,流程如下:
当引入缓存功能后,会引发出问题:(1)缓存穿透,(2)缓存击穿,(3)缓存雪崩。
二、缓存穿透
解释:缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
注意:不能避免低频的缓存穿透,可以避免高频的缓存穿透。
解决办法:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null存入缓存中,缺点:当用户不断地以大量不同的key(缓存层和数据库)去访问时,缓存层就会存储大量的(key-null),所以会占用过多的内存,此时需要设置缓存有效期,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
- 在server端存储一个布隆过滤器,将mysql包含的key放如布隆过滤器中,布隆过滤器能过滤一定不存在的数据。
四、缓存雪崩
解释:缓存层在同一时刻,大量缓存的数据同时失效,导致大量的请求跑到数据库。
导致原因:
(1)redis中缓存的数据有效期是一致的,然后失效也就是同一时刻。
解决办法:给每一条数据加上一个随机有效期,不要突然同时失效。
(2)redis数据库挂掉了
解决办法:分布式缓存。
五、缓存击穿
解释:缓存击穿是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。这种情况会导致数据库压力瞬间骤增,造成大量请求阻塞,甚至直接挂掉。
缓存击穿解决方案:
(1)第一种是设置key永不过期;
(2)第二种是使用分布式锁,保证同一时刻只能有一个查询请求重新加载热点数据到缓存中,这样,其他的线程只需等待该线程运行完毕,即可重新从Redis中获取数据。
第一种方式比较简单,在设置热点key的时候,不给key设置过期时间即可。不过还有另外一种方式也可以达到key不过期的目的,就是正常给key设置过期时间,不过在后台同时启一个定时任务去定时地更新这个缓存。
第二种方式使用了加锁的方式,锁的对象就是key,这样,当大量查询同一个key的请求并发进来时,只能有一个请求获取到锁,然后获取到锁的线程查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库。