所有栏目 | 云社区 美国云服务器[国内云主机商]
你的位置:首页 > 云社区 » 正文

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

发布时间:2020-04-15 16:13:19

资讯分类:redis  雪崩  穿透  并发  缓存  并发  雪崩
如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

Redis因其简单、高效的特点被广泛应用,如今中小型网站都在使用Redis了。我们在使用Redis时,在高并发场景下,Redis容易出现缓存并发、缓存穿透及雪崩的现象

缓存并发、缓存雪崩、缓存穿透这三者还是有区别的,我们先来了解一下。

1、缓存并发

我们说的缓存并发指的是多个Redis客户端同时SET Key时会引起并发问题。我们知道,Redis是单线程的,在多个Client并发操作时,秉承“先发起先执行”的原则,其它的处于阻塞状态

常见缓存并发有两种场景:

  • 缓存过期后会从后端数据库查询数据然后存入Redis缓存,但在高并发情况下可能在还没来得及将库中查出来的数据存入Redis时,其它Client又从数据库里查询数据再存入Redis了。这样一来会造成多个请求并发的从数据库获取数据,对后端数据库会造成压力。

  • 在高并发场景下,某个Key正在更新时,可能有很多Client在获取此Key的数据,这样会导致“脏数据”

如何解决缓存并发问题呢?我们常借助“锁”来实现,具体实现逻辑为:

在更新缓存或者读取过期缓存的情况下,我们先获取“锁”,当完成了缓存数据的更新后再释放锁,这样一来,其它的请求需要在释放锁之后执行,会牺牲一点时间。

2、缓存穿透

什么是缓存穿透现象呢?我们先来说下缓存正常的逻辑。

正常情况下我们都是根据缓存Key查看是否存在值或已过期,如果不存在我们则向后端数据库查询并将查询结果存入缓存中(数据库有结果时存入Redis,没结果时就没有存入Redis),下次请求时只要缓存没过期就直接从缓存中获取数据。

但是,如果我们在上一步过程中,从数据库查询出的结果是空的,所以没有存入Redis,然后又跑到后端数据库进行查询,即每一次请求都会落到数据库去查询,这样就导致了缓存命中率低的现象,这就是缓存穿透。

所以说缓存穿透可以理解为访问了一个不存在的Key,缓存无效,没有发挥作用,每次查询流量最终落在了数据库上面

还有一种情况,即使是一个存在的Key,在它缓存过期的那一瞬间,如果网站并发大,那在此Key没有重新生效时,所有的请求全部击穿到数据库上了

如果避免缓存穿透呢?最简单的方式就是即使数据库查询结果是空的,我们给一个缺省值或者直接将空值存入Redis

3、缓存雪崩

什么是缓存雪崩呢?缓存雪崩指的是在一段时间段内,Redis绝大部分Key集体失效的现象

造成这种现象的原因很简单:大量的Key设置的过期时间是相同的,或者都在同一段时间段内。当缓存在同一段时间段内集体失效后,你会发现数据库压力一下子就上去了(DB洪峰),最终引起雪崩现象。

如何避免呢?给两个方案参考:

  • 给所有缓存时间设置一个固定的TTL+随机时间TTL,这样能保证所有Key不会同一时间内集体过期;

  • 缓存副本机制,给每个Key设置一个副本,副本的TTL时间较长。


综上,在缓存并发情况严重时,热点Key在失效的瞬间会引起缓存击穿(小雪崩),如果所有的Key在同一时间内都失效了则会引起缓存雪崩现象,导致后端数据库压力聚增。

以上就是我的观点,对于这个问题大家是怎么看待的呢?欢迎在下方评论区交流 ~ 我是科技领域创作者,欢迎关注我了解更多科技知识!

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

缓存穿透

很多项目在使用Redis或其他缓存框架的时候,都是先查询缓存,查询不的话再查询数据库,查到之后再放到内存中;如果一个key值本身就不存在,那么每一次都会查询数据库,也就是常说的【缓存穿透】。

应对方法:

  • 如果在Redis中查询不到,并且查询数据库也没有结果,那么就将这个key写入到Redis中,value=空,并设置一个超时过期时间,例如五分钟,那么五分钟以内的对这个可以的所有查询就可以拦截下来,如果数据库有key对应的数据了,那么五分钟后Redis中的缓存过期,会访问数据库并加载缓存;但是如果被恶意攻击,每次请求的key都不相同且不存在,那么依然会穿透到数据库;

  • 布隆过滤器:将可能存在的数据Hash到一个足够大的bitmap上,它可以告诉你 “某个key一定不存在或者可能存在”,一个一定不存在的数据会被bitmap拦截。

缓存雪崩

很多时候,Redis中的缓存是要设置过期时间的,假如Redis中的数据,过期时间都设置成一样的,那么到了时间之后,全部缓存过期失效,下一秒所有的请求都会访问数据库,那么数据库可能因为访问量多大导致“崩溃”,这就是缓存雪崩。

应对方法:

  • 最暴力的解决办法,缓存不设置自动过期时间,只要缓存不崩,数据库就不会崩。

  • 另外一个办法,就是让缓存过期时间不那么一致,比如一批缓存数据24小时后过期,那么就在这个基础上,每条缓存的过期时间前后随机1-6000秒(1-10分钟)。

缓存并发

大多数时候,我们的程序访问Redis都不可能是单线程,那么当多个Client并发对Redis进行set key操作的时候,可能会产生一些问题;其实Redis本身是单线程的,这种时候会按照先后顺序进行操作;或者把操作放在队列中,按顺序执行;

但比如这种情况:

  1. token过期,有两个线程都去重新获取token;

  2. 线程1获取token1;

  3. 线程2获取到token2,此时token1过期;

  4. 线程1把token1放到Redis,再拿着token1去调用服务,发现过期了,继续去请求token3,此时token2过期;

  5. 线程2把token2放到Redis,再拿着token2去调用服务,发现过期了,继续去请求token4,此时token3过期;

  6. ... ...

这就需要我们在更新缓存的时候,做一些控制了。

我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

凌晨四点醒来看到这个问题,很有感触,索性觉也不睡了和大家好好聊聊这个话题,因为自己工作中遇到过缓存雪崩问题。缓存的概念以及使用我就不多说了,直接来聊干货!关注必回!


  • 缓存穿透和击穿,简单来说就是一个缓存数据被请求时没有直接从缓存中获取到值,转而绕过缓存,直接读取底层数据,中间层缓存的舒压作用没有得到体现,访问高峰期大批量的请求绕过缓存直接打到数据库上的情况。换一种更容易理解的表达方式,大部分缓存系统,都是按照key值去缓存查询,如果不存在对应的value,或者缓存读取超时,就会去DB中查找 。如果请求的并发量很大,就会对后端的DB系统造成很大的压力。这就叫做缓存穿透和击穿。事故缓存穿透和击穿的诱发原因大概率有以下几种。1.大量缓存同时过期,2.单台缓存服务器短时间内因为网络问题超时3.线程池链接被用尽!4.恶意穿透,恶意请求大量访问缓存中不存在的数据!

击穿和穿透场景不尽相同,击穿热点key在高峰期失效!穿透大概率指恶意访问缓存中不存在的key,故而解决方式也有差别,手机码字不易我就糅合在一起说。方案一,互斥锁排队处理,key获取value值为空时,锁上,从数据库中load数据后再释放锁。若其它线程获取锁失败,则等待一段时间后重试。方案二,网关中进行接口限流与熔断、降级。方案三,通过谷歌的布鲁过滤器进行快速筛选过滤

  • 缓存雪崩,最可怕的就是缓存雪崩,雪崩会导致一系列连锁反应。缓存由于某些原因(比如 宕机、cache服务挂了或者不响应)整体crash掉了,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。当然缓存穿透和击穿也有可能引起雪崩。
    解决方案有以下几种,方案一,多机房部署,服务高可用,即使某一机房因为外界因素挂掉也还有备用。方案二,熔断,根据一定规则判断缓存失效之后进行熔断降级处理。方案三,数据及时备份,发生事故之后快速恢复。

  • 缓存并发,这里的并发指的是多个redis的client同时set key引起的并发问题。比较有效的解决方案就是把redis.set操作放在队列中使其串行化。

关于缓存这块儿,还牵扯到数据一致性,缓存同步,redis集群等诸多重要知识点,手机码字,实属不易,以后再更新吧!大家对于缓存有独特见解的欢迎留言讨论!

分享让我们成长!

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

Redis缓存雪崩解决方案

由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会级联宕机的情况。缓存雪崩的英文原意是stampeding herd(奔逃的野牛),指的是缓存层宕掉后,流量会像奔逃的野牛一样,打向后端存储。

预防和解决缓存雪崩问题,可以从以下三个方面进行着手。
1)保证缓存层服务高可用性。和飞机都有多个引擎一样,如果缓存层设计成高可用的,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务,例如前面介绍过的Redis Sentinel和Redis Cluster都实现了高可用。
2)依赖隔离组件为后端限流并降级。无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源。作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部阻塞(hang)在这个资源上,造成整个系统不可用。降级机制在高并发系统中是非常普遍的:比如推荐服务中,如果个性化推荐服务不可用,可以降级补充热点数据,不至于造成前端页面是开天窗。在实际项目中,我们需要对重要的资源(例如Redis、MySQL、
HBase、外部接口)都进行隔离,让每种资源都单独运行在自己的线程池 中,即使个别资源出现了问题,对其他服务没有影响。但是线程池如何管理,比如如何关闭资源池、开启资源池、资源池阀值管理,这些做起来还是相当复杂的。这里推荐一个Java依赖隔离工具Hystrix(https://github.com/netflix/hystrix),如图11-15所示。Hystrix是解决依赖隔离的利器,但是该内容已经超出本书的范围,同时只适用于Java应用, 所以这里不会详细介绍。
3)提前演练。在项目上线前,演练缓存层宕掉后,应用以及后端的负载情况以及可能出现的问题,在此基础上做一些预案设定。

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

缓存 ,一定要具有个性化数据筛选的设置。这三个问题就能够解决了。换言之,缓存,是不具有通用性的。这是解决缓存诸多问题的关键。

如何解决Redis缓存雪崩、缓存穿透、缓存并发等问题?

缓存穿透、击穿与雪崩是常见的由于并发量大而导致的缓存问题。

#缓存穿透

缓存穿透指的是使用不存在的Key请求,导致缓存无法命中,每次请求都穿透到数据库,使数据库压力过大,甚至宕机。

通常解决方案是将空值缓存起来,再次接到同样的请求时,就可以命中缓存空值,直接返回而不再请求数据库。当然,如果每次的请求Key都不一样,导致空值缓存方案不起作用,这正常依赖于参数的合法性校验与数据摘要的校验,尽最大可能使用户无法修改请求参数。

更进一步的做法是针对高并发接口的数据使用布隆过滤器来判断请求的数据是否存在,如果不存在直接返回,避免数据库的查询操作。

什么是布隆过滤器

本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

(祥细原理在后续文章中介绍)

#缓存击穿

缓存击穿是指在高并发场景下,当某个热点Key过期时,大量的并发请求同时打到数据库,造成数据库压力巨大甚至打死的情况。

缓存击穿的处理方案有:

  • 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
  • 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
  • 若缓存的数据更新频繁或者缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动的重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。


#缓存雪崩

    缓存雪崩是指缓存重启或者大量的缓存数据集中在某一时间内失效,导致所有请求完全打到数据库,造成数据库负载巨大、压垮。

    解决方案就是避免大量缓存的Key集中式过期,如果需要集中,也应该设置偏差值。实现相对集中(如统一过期时间 + 随机秒数),而不是统一集中。

留言与评论(共有 0 条评论)
   
验证码:
Top