如何设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析

前言

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

缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

在流量大时,可能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,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

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

解决方案优点缺点
简单分布式锁(Tim yang) 1. 思路简单2. 保证一致性1. 代码复杂度增大2. 存在死锁的风险3. 存在线程池阻塞的风险
加另外一个过期时间(Tim yang) 1. 保证一致性同上 
不过期(本文)1. 异步构建缓存,不会阻塞线程池1. 不保证一致性。2. 代码复杂度增大(每个value都要维护一个timekey)。3. 占用一定的内存空间(每个value都要维护一个timekey)。
资源隔离组件hystrix(本文)1. hystrix技术成熟,有效保证后端。2. hystrix监控强大。  1. 部分访问存在降级策略。

四种方案来源网络,详文请链接:

http://carlosfu.iteye.com/blog/2269687

总结

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

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

来源:zeb_perfect

Image placeholder
IT头条
未设置
  50人点赞

没有讨论,发表一下自己的看法吧

推荐文章
从缓存穿透聊到布隆过滤器

缓存现在在web领域应用广泛,相信大部分开发人员都会用到,然而你遇见过缓存穿透吗? 什么是缓存穿透? 缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,但是出于容错的考虑,如果从存储层查

电商订单履约系统:你每天都在购物,却对它一无所知

01  订单履约概述1-什么是订单履约?订单是一次交易的生命周期,交易开始生成订单,结束的时候完成订单。在天猫或者京东上买东西,最终都会生成一张订单。其实,这个订单就是消费者与平台的一个简单的契约,而

如何设计 QQ、微信、微博、Github 等等,第三方账号登陆 ?(附表设计)

前言:多账户登陆1.创业初期用户名密码注册登陆手机号注册登陆2.数据库设计3.引入第三方账户方案4.数据库设计5.总结前言:多账户登陆互联网应用当中,我们的应用会使用多个第三方账号进行登录,比如:网易

Onvif/RTSP海康大华网络安防摄像机网页无插件直播方案EasyNVR登陆用户名密码失效问题解决方案

背景分析随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看、可控的诉求越来越多,互联网思维、架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础需

揭秘!闲鱼拉新投放系统如何设计

背景闲鱼目前已经是国内最大的闲置物品交易平台。随着闲鱼体量的增长和用户规模不断扩大,闲鱼App上的一个普通banner抑或是feeds中的一张普通的卡片,每天都可能被数以千万计的人看到。为了更好地服务

天翼云视频云储存解决方案,高效解决云储存难题

随着科技发展,视频监控迈向深度智能时代,前端摄像机开始内置深度学习算法,可以对人脸、车辆等关键信息进行快速定位抓拍,有效解决漏抓误报问题,解决了传统智能视频分析技术人工选择特征准确率低、浅层学习模型无

GoWeb教程_13.0. 如何设计一个 Web 框架

前面十二章介绍了如何通过Go来开发Web应用,介绍了很多基础知识、开发工具和开发技巧,那么我们这一章通过这些知识来实现一个简易的Web框架。通过Go语言来实现一个完整的框架设计,这框架中主要内容有第一

揭秘!一个高准确率的Flutter埋点框架如何设计

背景用户行为埋点是用来记录用户在操作时的一系列行为,也是业务做判断的核心数据依据,如果缺失或者不准确将会给业务带来不可恢复的损失。闲鱼将业务代码从Native迁移到Flutter上过程中,发现原先Na

1万属性,100亿数据,每秒10万吞吐,架构如何设计?

有一类业务场景,没有固定的schema存储,却有着海量的数据行数,架构上如何来实现这类业务的存储与检索呢?58最核心的数据“帖子”的架构实现技术细节,今天和大家聊一聊。一、背景描述及业务介绍什么是58

高并发业务场景下的秒杀解决方案 (初探)

文章简介 本文内容是对并发业务场景出现超卖情况而写的一片解决方案。主要是利用到了Redis中的队列技术。 超卖介绍 所谓的超卖,就是我们的售卖量大于了物品的库存量。该情况一般出现在电商系统中促销类的业

注册登录要点击两次终极解决方案

概述 社区vue基础教程用到自定义的v-validator指令,首次加载表单登录或注册页,都需要点击两次才能正常跳转对该问题,本文提供了两个解决办法,一个是settimeout,另一个则利用js事件

[解决方案] YDUI 香港服务器 IP 被墙,华东地区访问不了

域名是用的智能解析,华东地区全体阵亡被墙的IP:150.109.112.233香港腾讯云 被墙的IP:150.109.112.233香港腾讯云单独ping150.109.112.233解决方案:修改h

一个解决方案 四种架构 英特尔oneAPI来了

面向不断扩展的多元化计算需求,英特尔提出了全新的产品和技术战略,扎根于六大技术支柱——制程和封装,架构,内存和存储,互连,安全,软件。英特尔将通过横跨这六大技术的全方位计算创新,驱动计算性能的指数级提

高并发下的接口幂等性解决方案!

一、背景我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。例如:前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。我们发起一笔付款请求,应该只扣用户账户一

“拉通”内外资源 新华三解决方案部的终极使命

“在数字化转型时代,客户需要端到端的解决方案,新华三解决方案部要拉通内部所有产品线,形成场景化的解决方案。如果客户需要应用层面的解决方案,我们也要拉通ISV等生态伙伴与客户之间需求,打造无界生态,同时

华为发布智能数据解决方案FusionData

华为在北京发布智能数据解决方案FusionData,支持智能的数据全生命周期管理;从数据接入、数据处理和数据使能三个层面,重定义数据基础设施,帮助客户打造领先的智能数据解决方案,拥抱行业数字化,释放数

10分钟搞懂:亿级用户的分布式数据存储解决方案!

来源:IT进阶思维原创,转载请注明原出处内容提供:李智慧,前阿里巴巴技术专家,《大型网站技术架构》作者6月6日晚,林志玲与Akira公布婚讯、徐蔡坤祝福高考同学超常发挥,粉丝们百万的转发和点赞造成微博

云网融合已成ICT发展趋势 天翼云提供专业云网融合解决方案

随着全球云计算领域的飞速发展和我国云计算发展进入应用普及阶段,越来越多的企业已开始将企业信息系统等转移到云上。为了保障更多企业顺利上云,对网络和行业上云解决方案产生了新的需求,由此云网融合应运而生。运

从产品到解决方案,GaussDB与FusionData之我见

摘要:连接、计算和数据是ICT基础设施的三大基石,华为IT产品线副总裁、智能数据与存储领域总裁周跃峰在接受媒体采访时强调,这或许是华为在数据领域全面持续发力的原因之一。时隔不足一个月,华为连续召开两次

开创万兆组网时代 新华三商用万兆解决方案解读

随着网络技术的不断发展,越来越多的终端设备纷纷接入网络,用户对于高速网络的需求越来越高。网络速率也从10兆到百兆再到千兆一步步得以提升。我们享受了高速网络所带来的极大便利,但这些还远远不够……由于物联

助力平安城市 新华三安防监控解决方案解读

大家应该都有看过警匪片,在影视片段中警察通过视频监控能够对城市的各个角落进行全局监控,并对犯罪分子进行实时追踪。为城市治安防控以及案件侦破提供了极大便利。而在现实生活中,视频监控系统也的确有着非常重要

写数据库同时发mq消息事务一致性的一种解决方案

一、引子《事务注解(@Transactional)引起的数据覆盖故障》一文收到不少反馈。事务里不要有rpc,基本原则,sb封装的太好了,把很多人养傻了,function级别的事务,坑太大。网友一这个是

数据库分库分表解决方案汇总

一.数据切分关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索引,做很多操作时性能仍下降严重

腾讯云游戏行业整体解决方案

点击观看大咖分享随着游戏行业的迅猛发展,游戏行业竞争日益加剧,好的用户体验度和快速反应能力成为游戏网站发展的关键。游戏行业整体解决方案将能够为游戏厂商提供优质全面便捷的服务。腾讯云结合自身在云计算业务

RTSP-ONVIF协议安防视频监控流媒体服务解决方案EasyNVR在Windows重启时提示“进程意外终止”问题解析

什么是ONVIFOpenNetworkVideoInterfaceForum,开放型网络视频接口论坛,以公开、开放的原则共同制定开放性行业标准。是一个提供开放网络视频接口的论坛组织。ONVIF规范描述