精华内容
下载资源
问答
  • 缓存穿透,缓存击穿,缓存雪崩解决方案分析

    万次阅读 多人点赞 2017-01-06 11:12:50
    前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层...解决方案

    前言

    设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。

    缓存穿透

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

    解决方案

    有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    缓存雪崩

    缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

    解决方案

    缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

    缓存击穿

    对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

    缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

    解决方案

    1.使用互斥锁(mutex key)

    业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

    SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间,所以这里给出两种版本代码参考:

    //2.6.1前单机版本锁
    String get(String key) {  
       String value = redis.get(key);  
       if (value  == null) {  
        if (redis.setnx(key_mutex, "1")) {  
            // 3 min timeout to avoid mutex holder crash  
            redis.expire(key_mutex, 3 * 60)  
            value = db.get(key);  
            redis.set(key, value);  
            redis.delete(key_mutex);  
        } else {  
            //其他线程休息50毫秒后重试  
            Thread.sleep(50);  
            get(key);  
        }  
      }  
    }
    最新版本代码:

    public String get(key) {
          String value = redis.get(key);
          if (value == null) { //代表缓存值过期
              //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
    		  if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
                   value = db.get(key);
                          redis.set(key, value, expire_secs);
                          redis.del(key_mutex);
                  } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                          sleep(50);
                          get(key);  //重试
                  }
              } else {
                  return value;      
              }
     }
    memcache代码:

    if (memcache.get(key) == null) {  
        // 3 min timeout to avoid mutex holder crash  
        if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  
            value = db.get(key);  
            memcache.set(key, value);  
            memcache.delete(key_mutex);  
        } else {  
            sleep(50);  
            retry();  
        }  
    } 

    2. "提前"使用互斥锁(mutex key):

    在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。伪代码如下:

    v = memcache.get(key);  
    if (v == null) {  
        if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  
            value = db.get(key);  
            memcache.set(key, value);  
            memcache.delete(key_mutex);  
        } else {  
            sleep(50);  
            retry();  
        }  
    } else {  
        if (v.timeout <= now()) {  
            if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  
                // extend the timeout for other threads  
                v.timeout += 3 * 60 * 1000;  
                memcache.set(key, v, KEY_TIMEOUT * 2);  
      
                // load the latest value from db  
                v = db.get(key);  
                v.timeout = KEY_TIMEOUT;  
                memcache.set(key, value, KEY_TIMEOUT * 2);  
                memcache.delete(key_mutex);  
            } else {  
                sleep(50);  
                retry();  
            }  
        }  
    } 

    3. "永远不过期":  

    这里的“永远不过期”包含两层意思:

    (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

    (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

            从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

    String get(final String key) {  
            V v = redis.get(key);  
            String value = v.getValue();  
            long timeout = v.getTimeout();  
            if (v.timeout <= System.currentTimeMillis()) {  
                // 异步更新后台异常执行  
                threadPool.execute(new Runnable() {  
                    public void run() {  
                        String keyMutex = "mutex:" + key;  
                        if (redis.setnx(keyMutex, "1")) {  
                            // 3 min timeout to avoid mutex holder crash  
                            redis.expire(keyMutex, 3 * 60);  
                            String dbValue = db.get(key);  
                            redis.set(key, dbValue);  
                            redis.delete(keyMutex);  
                        }  
                    }  
                });  
            }  
            return value;  
    }

    4. 资源保护:

    采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

    四种解决方案:没有最佳只有最合适

    解决方案 优点 缺点
    简单分布式互斥锁(mutex key)

     1. 思路简单

    2. 保证一致性

    1. 代码复杂度增大

    2. 存在死锁的风险

    3. 存在线程池阻塞的风险

    “提前”使用互斥锁  1. 保证一致性 同上 
    不过期(本文)

    1. 异步构建缓存,不会阻塞线程池

    1. 不保证一致性。

    2. 代码复杂度增大(每个value都要维护一个timekey)。

    3. 占用一定的内存空间(每个value都要维护一个timekey)。

    资源隔离组件hystrix(本文)

    1. hystrix技术成熟,有效保证后端。

    2. hystrix监控强大。

     

     

    1. 部分访问存在降级策略。



    四种方案来源网络,详文请链接:http://carlosfu.iteye.com/blog/2269687?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

    总结

    针对业务系统,永远都是具体情况具体分析,没有最好,只有最合适。

    最后,对于缓存系统常见的缓存满了和数据丢失问题,需要根据具体业务分析,通常我们采用LRU策略处理溢出,Redis的RDB和AOF持久化策略来保证一定情况下的数据安全。

    展开全文
  • 缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx
  • 常见的缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx
  • 如何设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx
  • 缓存系统不得不考虑的问题是缓存穿透、缓存击穿与失效时的雪崩效应,这是避免不了的问题 。 今天我们来讨论下这几种问题的常见的解决思路 缓存穿透 what 顾名思义,是指业务请求穿过了缓存层,落到持久化存储上。在...
    展开全文
  • 服务雪崩+缓存雪崩+解决方案

    千次阅读 2020-02-09 17:55:10
    一、服务雪崩 1、定义: 服务堆积在同一个线程池中,因为所有的请求都是同一个线程池进行处理,这时候如果在高并发情况下,所有的请求全部访问同一个接口, 这时候可能会导致其他服务没有线程进行接受请求,这就是...

    一、服务雪崩

    1、定义:
    服务堆积在同一个线程池中,因为所有的请求都是同一个线程池进行处理,这时候如果在高并发情况下,所有的请求全部访问同一个接口,
    这时候可能会导致其他服务没有线程进行接受请求,这就是服务雪崩效应效应。

    2、原因:
    a.某几个机器故障:例如机器的硬驱动引起的错误,或者一些特定的机器上出现一些的bug(如,内存中断或者死锁)。
    b.服务器负载发生变化:某些时候服务会因为用户行为造成请求无法及时处理从而导致雪崩,例如阿里的双十一活动,若没有提前增加机器预估流量则会造服务器压力会骤然增大二挂掉。
    c.人为因素:比如代码中的路径在某个时候出现bug

    3、解决:4种:服务降级、服务熔断、服务隔离、限流模式、超时机制
    a.服务降级:在高并发情况下,防止用户一直等待(返回一个友好的提示,直接给客户端,不会去处理请求,调用fallBack本地方法),目的是为了用户体验。 秒杀---当前请求人数过多,请稍后重试
    b.服务熔断:熔断机制目的为了保护服务,在高并发的情况下,如果请求达到一定极限(可以自己设置阔值),如果流量超出了设置阈值,让后直接拒绝访问,保护当前服务。使用服务降级方式返回一个友好提示,服务熔断和服务降级一起使用。
    熔断的设计主要参考了hystrix的做法,分为3个模块:熔断请求判断算法、熔断恢复机制、熔断报警
     (1)熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。
     (2)熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。
     (3)熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警。
    c.服务隔离2种方式:线程池隔离和信号量隔离
     (1)线程池隔离:每个服务接口,都有自己独立的线程池,每个线程池互不影响。 
     (2)信号量隔离:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返
    回成功后计数器-1。
    d.限流模式:上述都属于出错后的容错处理机制,而限流模式则可以称为预防模式。限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。这种模式不能解决服务依赖的问题,只能解决系统整体资源分配问题,因为没有被限流的请求依然有可能造成雪崩效应。
    e.超时机制:超时分两种:请求的等待超时、请求运行超时
     (1)等待超时:在任务入队列时设置任务入队列时间,并判断队头的任务入队列时间是否大于超时时间,超过则丢弃任务。
     (2)运行超时:直接可使用线程池提供的get方法

    二、缓存雪崩

    1、定义:
    由于原有缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期。所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

                       缓存正常从Redis中获取

                                       缓存失效瞬间

    2、解决:5种:加锁排队、设置过期标志更新缓存、不同key设不同的缓存失效时间、二级缓存、消息中间件
    a.加锁排队(集群使用分布式锁,单机使用本地锁):在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许1个线程查询数据和写缓存,其他线程排队等待(集群分布式锁,单机本地锁)。减少服务器吞吐量,效率低。
    注意:加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法。
    b.设置过期标志更新缓存:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
     (1)缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存;
     (2)缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

                         加锁排队

              设置过期标志更新缓存


    c.不同key设不同的缓存失效时间:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
    d.二级缓存:A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
    e.消息中间件方式(推荐)
    消息中间件 可以解决高并发。如果大量的请求进行访问时候,Redis没有值的情况,会将查询的结果存放在消息中间件中(利用了MQ同步特性)
     

    转载:
    服务雪崩产生原因及解决办法

    解决或缓解服务雪崩的方案

    缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题!

    Redis雪崩效应以及解决方案

    展开全文
  • 1、什么是缓存雪崩?你有什么解决方案来防止缓存雪崩? 如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。... 防止缓存雪崩解决方案 1、加...

      1、什么是缓存雪崩?你有什么解决方案来防止缓存雪崩?
     
                如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
                由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU 和内存造成巨大压力,严重的会造成数据库宕机
     

      2. 防止缓存雪崩解决方案

       

     1、加锁排队   key: whiltList  value:1000w个uid 指定setNx whiltList value nullValue
                    mutex互斥锁解决,Redis的SETNX去set一个mutex key,
                    当操作返回成功时,再进行load db的操作并回设缓存;
                    否则,就重试整个get缓存的方法


                2、数据预热
                    缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,
                    先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
                    可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key     


                3、双层缓存策略
                    C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。


                
                4、定时更新缓存策略
                    失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存


                5、设置不同的过期时间,让缓存失效的时间点尽量均匀
     

    展开全文
  • Redis缓存穿透、缓存击穿、缓存雪崩解决方案和分析 缓存穿透 用户或黑客恶意大量访问缓存数据库中没有的数据,导致大量请求涌至关系型数据库,导致数据库宕机,这就是缓存穿透。 解决方案: (1)利用互斥锁,缓存...
  • 缓存雪崩是什么,怎么引起的,有什么危害? 缓存雪崩,一般是由大量的key集中在一段时间内失效,或者redis服务故障引起的,导致查询请求查不到缓存,大量的请求涌入mysql,它会导致mysql瞬间被大量的流量打死,或者是...
  • 1,什么是缓存雪崩 如果缓存集中在一段时间内失效,引发大量缓存穿透,所有的查询都落在数据库上,造成缓存雪崩,由于原有缓存失效,新缓存未到期间所有原本访问缓存的都去访问了数据库,而对数据库cpu和内存造成...
  • Redis缓存雪崩解决方案

    千次阅读 2019-03-13 23:49:28
    服务器雪崩的场景与解决方案 --转自头条号 优知学院 什么是应用服务雪崩 雪崩问题 分布式系统都存在这样一个问题,由于网络的不稳定性,决定了任何一个服务的可用性都不是 100% 的。当网络不稳定的时候,作为...
  • 什么是缓存雪崩 描述: 缓存雪崩一般是指,缓存层(例如redis集群)出现了异常,不能正常工作了。于是所有的请求都会达到存储层(数据库层),存储层的QPS会暴增,出现大量请求压垮存储层的情况。 当然如果缓存设置...
  • Redis缓存穿透和缓存雪崩 什么是缓存穿透? 当用户想要查找一个数据的时候,发现在Redis中没有查询到(没有命中缓存),于是就会向持久层数据库去查询数据。结果在持久层也没有查询到该数据。当很多用户都查询数据的...
  • 缓存一致性 缓存穿透 缓存雪崩解决方案 前言 一、缓存一致性解决方案 二、缓存穿透解决方案 三、缓存雪崩解决方案 四、缓存击穿解决方案 前言 高并发下如何解决使用缓存中出现的问题 一、缓存一致性解决方案 更新...
  • 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。 一、缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到...
  • 缓存雪崩:因为缓存服务挂掉或者热点缓存失效,从而导致所有的请求都去查数据库,导致数据库连接不够用或者数据库处理不过来,从而导致整个系统不可用。 数据库服务器压力大,依赖数据库的其他系统也会面临崩溃的...
  • 最简单粗暴的解决方案就是让热点key不设置过期时间,即key一直存在于缓存中,更新时直接覆盖即可。 设置定时任务检测要过期的key,然后在将要过期的时候重新从数据库把数据刷新到缓存中,这样的方式增加系统复杂度,...
  • 最简单粗暴的解决方案就是让热点key不设置过期时间,即key一直存在于缓存中,更新时直接覆盖即可。 设置定时任务检测要过期的key,然后在将要过期的时候重新从数据库把数据刷新到缓存中,这样的方式增加系统复
  • 解决思路: 采用锁 + 双重检查机制:某个key只让一个线程查询,阻塞其它线程,在同步块中,继续判断检查,保证不存在,才去查DB。 import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org....
  • 1.缓存击穿【Cache breakdown】 对于某些具有过期时间的key,如果这些键可以在某个时间点并发地访问,那么它就是一个非常“hot”的数据。...解决方案 互斥锁:业界更常见的做法是使用互斥锁。简单地说,当缓存失效时...
  • Redis缓存穿透、缓存击穿、缓存雪崩解决方案 缓存穿透 指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。 解决方案: i. ...
  • 缓存穿透、缓存击穿、缓存雪崩区别和解决方案

    万次阅读 多人点赞 2018-09-19 14:35:57
    一、缓存处理流程  前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。     二、缓存穿透  描述:  缓存...
  • redis缓存穿透,缓存击穿,缓存雪崩原因+解决方案
  • 前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的...解决方案 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,
  • 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑...解决方案 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,438
精华内容 9,775
关键字:

缓存雪崩解决方案