您的位置:首页 > 数据库 > Redis

关于【缓存穿透,缓存击穿,缓存雪崩,热点数据丢失】问题的解决方案

2019-03-27 11:23 921 查看

本篇文章转载自:石衫的架构笔记 微信公众号

前言

当我们系统中使用到了缓存时,不管是一级缓存还是多级缓存,它的作用就是业务系统在请求数据时,如果缓存中有,就从缓存中获取;如果缓存中没有,再去访问DB,从而减少DB的压力,缩短获取数据的时间。那么在使用缓存的过程中,会出现哪些常见问题?我们又如何来解决?

1 缓存穿透

1.1 什么是缓存穿透

正常情况下,我们去查询数据都是存在的。

那么请求查询一条压根在数据库中就不存在的数据,即在缓存和数据库中都没有这条数据,但是请求每次在缓存中找不到,就去访问数据库,比较占用和消耗数据库的资源,这种现象,就叫做缓存穿透。

1.2 穿透带来的问题

黑客攻击系统,通过缓存穿透,使用一个不存在的id,大量访问系统的接口,系统因为缓存中没有数据,而去大量的访问数据库,导致数据库资源被无效占用,影响甚至拖垮正常业务。

1.3 解决办法

1.3.1 缓存空值

我们知道,缓存服务器(redis)一般是通过key value来存储缓存数据的。当有请求过来,会通过key在缓存中查找数据。而发生缓存击穿的问题,是因为缓存服务器中没有存储这些无效数据的key,从而导致无效请求访问到数据库。

那么我们可以在这些请求访问数据库后,没有得到查询结果时,在缓存服务器中存储这个key,并设置它的值为null,这样当再次发生查询这个key的请求时,缓存服务器就直接返回null,不去访问数据库了。

但是不要忘记,设置缓存失效时间,方便正确数据的录入和更新。

1.3.2 BloomFilter

BloomFilter类似于一个hbase set用来判断某个元素(key)是否存在于某个集合中。

这种方式在大数据应用场景比较多,比如Hbase(HBase是一个分布式的、面向列的开源数据库)中使用它去判断数据是否在磁盘上。还有在爬虫场景判断url是否已经被爬取过。

这种方式可以加在第一种方案中,在缓存之前再加一层BloomFilter去查询key是否存在,如果不存在直接返回,存在再去查询缓存或者DB。

1.4 如何选择

针对大量恶意请求攻击,如果key重复多,使用第一种方案是有效的。

如果恶意请求key不重复的多,则使用第二种方案最有效。

2 缓存击穿

2.1 什么是缓存击穿

在平常高并发的系统中,大量的请求同事查询到一个key时,此时这个key正好失效了,就会导致大量的请求都转到数据库上面了,这种现象我们成为缓存击穿。

2.2 会带来什么问题

会造成数据库访问量突然增大,压力剧增。

2.3 如何解决

上面的现象是多个线程同时去查询数据库的这条数据,那么我可以在第一个查询数据的请求上使用一个 互斥锁 来锁住它。

其他的线程走到这一步拿不到锁就等待,等到第一个线程查询到了数据,然后做缓存,后面的线程进来发现已经有缓存了,就可以直接获取缓存,而不访问数据库了。

3 缓存雪崩

3.1 什么是缓存雪崩

在当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求直接请求数据库上,结果数据库就撑不住了,也跟着挂掉了。

3.2 解决办法

3.2.1 事前:

使用集群缓存,保证缓存服务器的高可用。

比如使用redis,就要考虑到 主 从 + 哨兵和 集群 来避免redis的全面崩盘的情况。

3.2.2 事中:

ehcahe本地缓存 + Hystrix限流&降级,避免DB被打死

使用ehcahe本地缓存的目的也是考虑在redis集群完全不可用的时候,ehcahe本地缓存还能支撑一阵。

使用Hystrix进行限流&降级,比如一秒来了5000个请求,我们假设只能有一秒2000个请求通过这个组件,那么其他3000个请求就会走限流逻辑。

然后去调用我们自己开发的降级组件,比如设置一些默认值之类的,以此来保护DB不会被大量请求搞的宕机。

3.2.3 事后:

开启redis的持久化机制,恢复缓存服务器集群

缓存服务器重启,根据配置的持久化策略,会读取日志文件,恢复内存中的数据。

4 解决热点数据集中失效的问题

我们在设置缓存的时候,一般也会设置缓存的失效时间,过了这个时间,缓存失效了。

如果这批数据,是请求量比较高的,那么请求就会转到数据库去,数据库压力瞬间增大,性能下降甚至宕机。

就是缓存击穿现象。

4.1 解决办法

4.1.1 设置不同的失效时间

避免热点数据集中失效,在设置缓存时间时,我们让它们的失效时间错开。比如在一个基础的时间上加上或减去一个范围内的随机值。

4.1.2 互斥锁

结合上面提到的请求,在高并发请求中,在第一个请求去查询数据库的时候对它加一个互斥锁,其余的查询请求都会被组塞住,直到锁被释放,从而保护数据库。

但是也是因为它会阻塞其他线程,系统的吞吐量也会下降,需要结合业务中的实际需要来考虑要不要这么做。

 

(adsbygoogle = window.adsbygoogle || []).push({});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Hystrix Redis Apache HBase