缓存穿透
很多项目在使用Redis或其他缓存框架的时候,都是先查询缓存,查询不的话再查询数据库,查到之后再放到内存中;如果一个key值本身就不存在,那么每一次都会查询数据库,也就是常说的【缓存穿透】。
应对方法:
如果在Redis中查询不到,并且查询数据库也没有结果,那么就将这个key写入到Redis中,value=空,并设置一个超时过期时间,例如五分钟,那么五分钟以内的对这个可以的所有查询就可以拦截下来,如果数据库有key对应的数据了,那么五分钟后Redis中的缓存过期,会访问数据库并加载缓存;但是如果被恶意攻击,每次请求的key都不相同且不存在,那么依然会穿透到数据库;
布隆过滤器:将可能存在的数据Hash到一个足够大的bitmap上,它可以告诉你 “某个key一定不存在或者可能存在”,一个一定不存在的数据会被bitmap拦截。
凌晨四点醒来看到这个问题,很有感触,索性觉也不睡了和大家好好聊聊这个话题,因为自己工作中遇到过缓存雪崩问题。缓存的概念以及使用我就不多说了,直接来聊干货!关注必回!
- 缓存穿透和击穿,简单来说就是一个缓存数据被请求时没有直接从缓存中获取到值,转而绕过缓存,直接读取底层数据,中间层缓存的舒压作用没有得到体现,访问高峰期大批量的请求绕过缓存直接打到数据库上的情况。换一种更容易理解的表达方式,大部分缓存系统,都是按照key值去缓存查询,如果不存在对应的value,或者缓存读取超时,就会去DB中查找 。如果请求的并发量很大,就会对后端的DB系统造成很大的压力。这就叫做缓存穿透和击穿。事故缓存穿透和击穿的诱发原因大概率有以下几种。1.大量缓存同时过期,2.单台缓存服务器短时间内因为网络问题超时3.线程池链接被用尽!4.恶意穿透,恶意请求大量访问缓存中不存在的数据!
击穿和穿透场景不尽相同,击穿热点key在高峰期失效!穿透大概率指恶意访问缓存中不存在的key,故而解决方式也有差别,手机码字不易我就糅合在一起说。方案一,互斥锁排队处理,key获取value值为空时,锁上,从数据库中load数据后再释放锁。若其它线程获取锁失败,则等待一段时间后重试。方案二,网关中进行接口限流与熔断、降级。方案三,通过谷歌的布鲁过滤器进行快速筛选过滤。
- 缓存雪崩,最可怕的就是缓存雪崩,雪崩会导致一系列连锁反应。缓存由于某些原因(比如 宕机、cache服务挂了或者不响应)整体crash掉了,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。当然缓存穿透和击穿也有可能引起雪崩。
解决方案有以下几种,方案一,多机房部署,服务高可用,即使某一机房因为外界因素挂掉也还有备用。方案二,熔断,根据一定规则判断缓存失效之后进行熔断降级处理。方案三,数据及时备份,发生事故之后快速恢复。 - 缓存并发,这里的并发指的是多个redis的client同时set key引起的并发问题。比较有效的解决方案就是把redis.set操作放在队列中使其串行化。
关于缓存这块儿,还牵扯到数据一致性,缓存同步,redis集群等诸多重要知识点,手机码字,实属不易,以后再更新吧!大家对于缓存有独特见解的欢迎留言讨论!
分享让我们成长!
Redis因其简单、高效的特点被广泛应用,如今中小型网站都在使用Redis了。我们在使用Redis时,在高并发场景下,Redis容易出现缓存并发、缓存穿透及雪崩的现象。
缓存并发、缓存雪崩、缓存穿透这三者还是有区别的,我们先来了解一下。
1、缓存并发
我们说的缓存并发指的是多个Redis客户端同时SET Key时会引起并发问题。我们知道,Redis是单线程的,在多个Client并发操作时,秉承“先发起先执行”的原则,其它的处于阻塞状态。
常见缓存并发有两种场景:
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)提前演练。在项目上线前,演练缓存层宕掉后,应用以及后端的负载情况以及可能出现的问题,在此基础上做一些预案设定。
缓存穿透、击穿与雪崩是常见的由于并发量大而导致的缓存问题。
#缓存穿透
缓存穿透指的是使用不存在的Key请求,导致缓存无法命中,每次请求都穿透到数据库,使数据库压力过大,甚至宕机。
通常解决方案是将空值缓存起来,再次接到同样的请求时,就可以命中缓存空值,直接返回而不再请求数据库。当然,如果每次的请求Key都不一样,导致空值缓存方案不起作用,这正常依赖于参数的合法性校验与数据摘要的校验,尽最大可能使用户无法修改请求参数。
更进一步的做法是针对高并发接口的数据使用布隆过滤器来判断请求的数据是否存在,如果不存在直接返回,避免数据库的查询操作。
缓存 ,一定要具有个性化数据筛选的设置。这三个问题就能够解决了。换言之,缓存,是不具有通用性的。这是解决缓存诸多问题的关键。