如果我们使用ID进行查询,当更新或者从数据库获

3 缓存雪崩

2- 缓存击穿

也叫缓存穿透,查询一个数据库中不存在的数据,比如商品详情,查询一个不存在的ID,每次都会访问DB,可能不会被关注,但是这个却是造成数据库高负载的元凶。

此外,从整个研发体系流程的角度,应该加强压力测试,尽量模拟真实场景,尽早的暴露问题从而防范。

使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

解决方案

业内推荐的做法是通过一致性Hash算法来解决。

我们会想到类似“锁”的机制,在缓存更新或者过期的情况下,先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据。

分布式锁

不管我们使用的哪种缓存产品,基本上都会遇到缓存击穿、缓存失效以及热点key的问题。一旦出现上述问题,如洪水般的请求会涌向后台DB服务器,轻则应用迟缓,重则整个应用系统瘫痪。

该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached 节点就已经达3000 个,缓存数千 G 内容。

也可以通过异步更新服务来更新设置软过期的缓存,这样应用层就不用关心缓存并发的问题了。

解决方法

场景1:导致这种现象的原因有很多种,上面提到的“缓存并发”,“缓存穿透”,“缓存颠簸”等问题,其实都可能会导致缓存雪崩现象发生。

根据上面的解决方法来避免雪崩的发生

场景2: 缓存冷启动或者大量缓存同时失效,例如某个时间点内,系统预加载的缓存周期性集中失效了,也可能会导致雪崩。

解决方案:

  1. 为了解决冷启动的问题,启动时,先预热缓存,根据实际业务估算将数据从数据库总load到缓存中,注意要分批次load,防止DB崩溃。
  2. 为了避免这种周期性大量缓存同时失效,可以通过设置不同的过期时间,来错开缓存过期,从而避免缓存集中失效。

图片 1

有些业务数据可能适合Hash分布,而有些业务适合采用范围分布,这样能够从一定程度避免网络IO的开销。

软过期指对缓存中的数据设置失效时间,就是不使用缓存服务提供的过期时间,而是业务层在数据中存储过期时间信息,由业务程序判断是否过期并更新,在发现了数据即将过期时,将缓存的时效延长,程序可以派遣一个线程去数据库中获取最新的数据,其他线程这时看到延长了的过期时间,就会继续使用旧数据,等派遣的线程获取最新数据后再更新缓存。

3- 缓存颠簸

缓存的颠簸问题,有些地方可能被成为“缓存抖动”,可以看做是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响。一般是由于缓存节点故障导致。

缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程。但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 “雪崩”现象。

我们通常有3种方式来解决这个问题。

解决方案

使用锁机制(缓存服务器集群环境下,使用分布式锁),在缓存更新或者过期的情况下,先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据,效率较高。可在缓存更新方法上加上sychronized修饰。

在高并发场景下,如果某一个key被高并发访问,没有被命中,出于对容错性考虑,会尝试去从后端数据库中获取,从而导致了大量请求达到数据库,而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操作,从而导致巨大冲击和压力。

通常的解决办法是对不同的数据使用不同的失效时间,甚至对相同的数据、不同的请求使用不同的失效时间,例如,我们要缓存user数据,会对每个用户的数据设置不同的缓存过期时间,可以定义一个基础时间,假设10秒,然后加上一个两秒以内的随机数,过期时间为10~12秒,就会避免缓存雪崩。

4- 热点Key

缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。

3、数据访问方式

缓存雪崩指缓存服务器重启或者大量缓存集中在某一个时间段内失效,给后端数据库造成瞬时的负载升高的压力,甚至压垮数据库的情况。

解决方案
  1. 缓存空对象
    当通过某一个key去查询数据的时候,如果对应在数据库中的数据都不存在,我们将此key对应的value设置为一个默认的值,比如“NULL”,并设置一个缓存的失效时间,这时在缓存失效之前,所有通过此key的访问都被缓存挡住了。后面如果此key对应的数据在DB中存在时,缓存失效之后,通过此key再去访问数据,就能拿到新的value了。这种方式实现起来成本较低,比较适合命中不高,但可能被频繁更新的数

  2. 单独过滤处理
    对所有可能对应数据为空的key进行统一的存放,并在请求前做拦截,这样避免请求穿透到后端数据库。这种方式实现起来相对复杂,比较适合命中不高,但是更新不频繁的数据。

这就比较依赖缓存的过期和更新策略。一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存。

软过期

解决方案
  1. 客户端热点key缓存:将热点key对应value并缓存在客户端本地,并且设置一个失效时间。对于每次读请求,将首先检查key是否存在于本地缓存中,如果存在则直接返回,如果不存在再去访问分布式缓存的机器。
  2. 服务端负载均衡:将热点key复制多个副本,然后存储到缓存集群的不同机器上。当通过热点key去查询数据时,通过某种hash算法随机选择一个副本机器访问缓存,将热点分散到了不同机器上。

二、缓存并发问题

0 题记

1- 缓存并发更新控制

一个共享缓存失效后,接下来有多个线程尝试从后台数据库服务器获取数据来更新缓存时,因为只需要一个线程完成从数据库中取数据然后在放在缓存内即可,然后其他线程再去取这个缓存,并需要并发的更新这个缓存。

图片 2

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

5- 缓存雪崩

缓存雪崩就是指由于缓存的原因,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。

当然,缓存无底洞现象并不常见。在绝大多数的公司里可能根本不会遇到。

我们通常将空值缓存起来,再次接收到同样的查询请求时,若命中缓存并且值为空,就会直接返回,不会透传到数据库,避免缓存穿透。当然,有时恶意袭击者可以猜到我们使用了这种方案,每次都会使用不同的参数来查询,这就需要我们对输入的参数进行过滤,例如,如果我们使用ID进行查询,则可以对ID的格式进行分析,如果不符合产生ID的规则,就直接拒绝,或者在ID上放入时间信息,根据时间信息判断ID是否合法,或者是否是我们曾经生成的ID,这样可以拦截一定的无效请求。

三、缓存穿透问题

1 缓存穿透

图片 3

缓存穿透、缓存并发和缓存雪崩是常见的由于并发量大而导致的缓存问题,本文讲解其产生原因和解决方案。

他们发现了一个问题---memcached 连接频率,效率下降了,于是加 memcached 节点,添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为”无底洞现象”。

缓存并发的问题通常发生在高并发的场景下,当一个缓存key过期时,因为访问这个缓存key的请求量较大,多个请求同时发现缓存过期,因此多个请求会同时访问数据库来查询最新数据,并且回写缓存,这样会造成应用和数据库的负载增加,性能降低,由于并发较高,甚至会导致数据库被压死。

六、缓存无底洞现象

与分布式锁类似,我们通过本地锁的方式来限制只有一个线程去数据库中查询数据,而其他线程只需等待,等前面的线程查询到数据后再访问缓存。但是,这种方法只能限制一个服务节点只有一个线程去数据库中查询,如果一个服务有多个节点,则还会有多个数据库查询操作,也就是说在节点数量较多的情况下并没有完全解决缓存并发的问题。

缓存的颠簸问题,有些地方可能被成为“缓存抖动”,可以看做是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响。一般是由于缓存节点故障导致。业内推荐的做法是通过一致性Hash算法来解决。

本文由必威发布于必威-编程,转载请注明出处:如果我们使用ID进行查询,当更新或者从数据库获

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。