留言与评论(共有 0 条评论) |
发布时间:2020-04-15 16:13:19
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或其他缓存框架的时候,都是先查询缓存,查询不的话再查询数据库,查到之后再放到内存中;如果一个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本身是单线程的,这种时候会按照先后顺序进行操作;或者把操作放在队列中,按顺序执行;
但比如这种情况:
token过期,有两个线程都去重新获取token;
线程1获取token1;
线程2获取到token2,此时token1过期;
线程1把token1放到Redis,再拿着token1去调用服务,发现过期了,继续去请求token3,此时token2过期;
线程2把token2放到Redis,再拿着token2去调用服务,发现过期了,继续去请求token4,此时token3过期;
... ...
这就需要我们在更新缓存的时候,做一些控制了。
凌晨四点醒来看到这个问题,很有感触,索性觉也不睡了和大家好好聊聊这个话题,因为自己工作中遇到过缓存雪崩问题。缓存的概念以及使用我就不多说了,直接来聊干货!关注必回!
击穿和穿透场景不尽相同,击穿热点key在高峰期失效!穿透大概率指恶意访问缓存中不存在的key,故而解决方式也有差别,手机码字不易我就糅合在一起说。方案一,互斥锁排队处理,key获取value值为空时,锁上,从数据库中load数据后再释放锁。若其它线程获取锁失败,则等待一段时间后重试。方案二,网关中进行接口限流与熔断、降级。方案三,通过谷歌的布鲁过滤器进行快速筛选过滤。
关于缓存这块儿,还牵扯到数据一致性,缓存同步,redis集群等诸多重要知识点,手机码字,实属不易,以后再更新吧!大家对于缓存有独特见解的欢迎留言讨论!
分享让我们成长!
由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会级联宕机的情况。缓存雪崩的英文原意是stampeding herd(奔逃的野牛),指的是缓存层宕掉后,流量会像奔逃的野牛一样,打向后端存储。
缓存 ,一定要具有个性化数据筛选的设置。这三个问题就能够解决了。换言之,缓存,是不具有通用性的。这是解决缓存诸多问题的关键。
缓存穿透、击穿与雪崩是常见的由于并发量大而导致的缓存问题。
#缓存穿透
缓存穿透指的是使用不存在的Key请求,导致缓存无法命中,每次请求都穿透到数据库,使数据库压力过大,甚至宕机。
通常解决方案是将空值缓存起来,再次接到同样的请求时,就可以命中缓存空值,直接返回而不再请求数据库。当然,如果每次的请求Key都不一样,导致空值缓存方案不起作用,这正常依赖于参数的合法性校验与数据摘要的校验,尽最大可能使用户无法修改请求参数。
更进一步的做法是针对高并发接口的数据使用布隆过滤器来判断请求的数据是否存在,如果不存在直接返回,避免数据库的查询操作。
什么是布隆过滤器
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
(祥细原理在后续文章中介绍)
#缓存击穿
缓存击穿是指在高并发场景下,当某个热点Key过期时,大量的并发请求同时打到数据库,造成数据库压力巨大甚至打死的情况。
缓存击穿的处理方案有:
#缓存雪崩
缓存雪崩是指缓存重启或者大量的缓存数据集中在某一时间内失效,导致所有请求完全打到数据库,造成数据库负载巨大、压垮。
解决方案就是避免大量缓存的Key集中式过期,如果需要集中,也应该设置偏差值。实现相对集中(如统一过期时间 + 随机秒数),而不是统一集中。
留言与评论(共有 0 条评论) |
全站搜索