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

redis三个缓存问题 (缓存雪崩,缓存击穿,缓存穿透)

2020-07-16 04:35 525 查看

今天来看看redis三个缓存问题 (缓存雪崩,缓存击穿,缓存穿透)

还是按照常规,来一张期待已久的图片

简单说一下上面数据的流程:
当用户发出一个请求,服务器会对其进行解析,然后就是去查找数据啦
第一步去reids缓存查找数据,如果查到想要的数据就返回给用户
如果在reids缓存汇总没有查找到数据,则进入mysql数据库查找数据,然后把数据结果返回给用户,同时将该数据写进redis

redis缓存雪崩

定义:
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,造成数据库后端故障,从而引起应用服务器雪崩。

雪崩效应产生的几种场景

流量激增:比如异常流量、用户重试导致系统负载升高;
缓存刷新:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
程序有Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
硬件故障:比如宕机,机房断电,光纤被挖断等。
数据库严重瓶颈,比如:长事务、sql超时等。
线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引发雪崩;

一个典型的场景:

马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。

解决方案

1、避免缓存集中失效,不同的key设置不同的超时时间,设置不同的超时时间,那么就不会同时失效,这样不会造成大面积的失效
2、提高缓存的HA,如:redis集群,将热点数据均匀分布在不同搞得缓存数据库中。
3、设置热点数据永远不过期。
4、限流&降级
5、用一个队列让请求量没有那么答。

雪崩的整体解决方案

(1)熔断模式

这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

重点监控的机器性能指标
cpu(Load)
cpu使用率/负载
memory 内存
mysql监控长事务(这里与sql查询超时是紧密结合的,需要重点监控)
sql超时 线程数等
总之,除了cpu、内存、线程数外,重点监控数据库端的长事务、sql超时等,绝大多数应用服务器发生的雪崩场景,都是来源于数据库端的性能瓶颈,从而先引起数据库端大量瓶颈,最终拖累应用服务器也发生雪崩,最后就是大面积的雪崩。

(2)隔离模式

这种模式就像对系统请求按类型划分成一个个小岛的一样,当某个小岛被火少光了,不会影响到其他的小岛。

例如可以对不同类型的请求使用线程池来资源隔离,每种类型的请求互不影响,如果一种类型的请求线程资源耗尽,则对后续的该类型请求直接返回,不再调用后续资源。这种模式使用场景非常多,例如将一个服务拆开,对于重要的服务使用单独服务器来部署,再或者公司最近推广的多中心。

(3)限流模式

上述的熔断模式和隔离模式都属于出错后的容错处理机制,而限流模式则可以称为预防模式。限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。这种模式不能解决服务依赖的问题,只能解决系统整体资源分配问题,因为没有被限流的请求依然有可能造成雪崩效应。

redis缓存击穿

我们知道每次请求先查询redis,如果reids中不存在你想要的数据就去请求数据库。
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。如果在大流量下数据库可能挂掉。

解决方案

1、异步构建缓存

在这种方案下,构建缓存采取异步策略,会从线程池中取线程来异步构建缓存,从而不会让所有的请求直接怼到数据库上。该方案redis自己维护一个timeout,当timeout小于System.currentTimeMillis()时,则进行缓存更新,否则直接返回value值。

2、使用互斥锁
该方法是比较普遍的做法,即,在根据key获得的value值为空时,先锁上,再从数据库加载,加载完毕,释放锁。若其他线程发现获取锁失败,则睡眠50ms后重试。

至于锁的类型,单机环境用并发包的Lock类型就行,集群环境则使用分布式锁( redis的setnx)

3、设置热点数据永远不过期。

redis缓存穿透

定义
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

解决方案
1、接口层增加校验:如用户鉴权校验,id做基础校验,id<=0的直接拦截;

2、 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

3、 布隆过滤器
布隆过滤器的巨大用处就是,能够迅速判断一个元素是否在一个集合中,所以在redis和mysql之间加一个布隆过滤器,就像这样,如果布隆过滤器中不存在就没必要去mysql啦

布隆过滤器的应用场景:

1、网页爬虫对URL的去重,避免爬取相同的URL地址

2、反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信)

3、缓存击穿,将已存在的缓存放到布隆过滤器中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。

布隆过滤器原理

布隆过滤器说存在那可能存在,如果布隆过滤器说不存在那一定不存在

布隆过滤器的底层是一个bit的数组,存放0/1数组

添加元素:
如果需要添加一个元素

1.对元素进行多个无偏哈希函数对元素进行哈希(在这里我定义为A、B、C)
2.对hash结果进行取余,使三次得到的数据不同,并改变对应位置的值为1
3.添加就结束啦

查询元素是不是存在

1、用同样的方法对元素进行三次哈希
2、得到对应数组中的位置
3、判断对应位置是不是都是1,如果是那元素有可能存在,如果不都是1,那元素一定不存在

————————————————
版权声明:本文为CSDN博主「flying$」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: