Redis 实战 - 缓存异常及解决方案

在这里插入图片描述

文章目录

  • 概述
  • 一、缓存穿透
    • 1.1 缓存穿透是什么
    • 1.2 解决方案
  • 二、缓存击穿
    • 2.1 缓存击穿是什么
    • 2.2 解决方案
  • 三、缓存雪崩
    • 3.1 缓存雪崩是什么
    • 3.2 解决方案
  • 四、拓展
    • 4.1 缓存预热
    • 4.2 缓存降级
  • 五、结语

把今天最好的表现当作明天最新的起点…….~

概述

  在实际的业务场景中,Redis 一般和其他数据库搭配使用,比如和关系型数据库 MySQL 配合使用,用来减轻后端数据库的压力。Redis 会把 MySQL 中经常被查询的数据缓存起来,比如热点数据,这样当用户来访问的时候,就不需要到 MySQL 中去查询,而是直接获取 Redis 中的缓存数据,从而降低了后端数据库的读取压力。如果说用户查询的数据在 Redis 没有找到,那么用户的查询请求就会被转到 MySQL 数据库。当 MySQL 将查询到的数据返回给客户端时,同时也会将数据缓存到 Redis 中,这样用户再次读取时,就可以直接从 Redis 中获取数据。流程图如下所示:
在这里插入图片描述
  在使用 Redis 作为缓存数据库的过程中,有时也会遇到一些棘手问题,比如常见缓存穿透、缓存击穿和缓存雪崩等问题,如下图所示。本文中将对这些问题做简单地说明,并且提供有效的解决方案。

Redis 缓存异常
缓存穿透
缓存击穿
缓存雪崩

一、缓存穿透

1.1 缓存穿透是什么

  一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,那么就去数据库去查找。当用户查询某个数据时,Redis 中不存在该数据,也就是缓存没有命中,此时查询请求就会转向数据库,结果发现数据库中也不存在该数据,数据库只能返回一个空对象(相当于进行了两次无用的查询)。用户拿不到数据时,就会一直发请求查询数据库,这样会对数据库的访问造成很大的压力。如果这种类请求非常多,或者用户利用这种请求进行恶意攻击,就会给数据库造成很大压力,甚至于崩溃,这种现象就叫缓存穿透。
在这里插入图片描述

  这种现象的原因其实很好理解,当客户端访问不存在的数据时,先请求 Redis,但是此时 Redis 中并没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库。我们都知道数据库能够承载的并发不如 Redis 这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库。

1.2 解决方案

  简单的解决方案就是当Redis、数据库中都没有值返回空对象时, 可以在 Redis 中存放一个空值,同时为其设置一个过期时间。这样,当用户再次发起相同请求访问这个不存在的数据,那么就会从缓存中拿到一个空对象,用户的请求被阻断在了缓存层。这样就可以减少重复查询空值引起的系统压力增大,从而从而保护了后端数据库。示例代码如下:

private String queryMessager(String key){
    // 从缓存中获取数据
    String message = getFromCache(key);
    // 如果缓存中没有 从数据库中查找
    if(StringUtils.isBlank(message)){
        message = getFromDb(key);
        // 如果数据库中也没有数据 就设置短时间的缓存
        if(StringUtils.isBlank(message)){
            // 设置缓存时间(缓存的key,缓存的值,失效时间:单位秒)
            redisClient.setNxEx(key,null,60);
        } else {
            redisClient.setNxEx(key,message,1800);
        }
    }
    return message;
}

  这种做法虽然优化了缓存穿透问题,但也存在一些问题。虽然请求进不了数据库,但是会占用 Redis 的缓存空间。而大量的空缓存导致资源的浪费,也有可能导致 Redis 和数据库中的数据不一致。

二、缓存击穿

2.1 缓存击穿是什么

  我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频地访问的数据被称为热点数据。比如某个热点数据,它无时无刻都在接受大量的并发访问,如果在某一时刻忽然过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,导致大量的并发请求直接访问数据库,就像在一个完好无损的桶上凿开了一个洞,引起数据库压力瞬间增大,这种现象被称为缓存击穿。
在这里插入图片描述

  缓存击穿一般出现在高并发系统中,是大量并发用户同时请求到缓存中没有但数据库中有的数据,也就是同时读缓存没读到数据,又同时去数据库去取数据。由于请大量请求同时过来,来不及更新缓存就全部打到数据库那边,引起数据库压力瞬间增大。

2.2 解决方案

  • 将热点数据设置加上互斥锁
    • 此方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。当第一个数据库查询请求发起后,就将缓存中该数据上锁;此时到达缓存的其他查询请求将无法查询该字段,从而被阻塞等待;当第一个请求完成数据库查询,并将数据更新值缓存后,释放锁;此时其他被阻塞的查询请求将可以直接从缓存中查到该数据。

      private ReentrantLock reentrantLock = new ReentrantLock();
      public static String getData(String key) throws InterruptedException {
          // 从 Redis 查询数据
          String result = getDataByKey(key);
          // 参数校验
          if (StringUtils.isBlank(result)) {
              // 获取锁
              if (reentrantLock.tryLock()) {
                  // 去数据库查询
                  result = getDataByDB(key);
                  // 校验
                  if (StringUtils.isNotBlank(result)) {
                      // 搞进缓存
                      setDataToKey(key, result);
                  }
                  // 释放锁,正常会在finally中释放
                  reentrantLock.unlock();
              } else {
                  // 稍等一下
                  Thread.sleep(100L);
                  result = getData(key);
              }
          }
          return result;
      }
      
    • 当某一个热点数据失效后,只有第一个数据库查询请求发往数据库,其余所有的查询请求均被阻塞,从而保护了数据库。但是,由于采用了互斥锁,其他请求将会阻塞等待,可能会存在死锁和线程池阻塞的风险,此时系统的吞吐量将会下降,这需要结合实际的业务考虑是否允许这么做。

  • 将热点数据设置为永远不过期
    • 当向缓存中存储这些数据的时候,可以将他们的缓存失效时间错开,这样能够避免同时失效。如在一个基础时间上加/减一个随机数,从而将这些缓存的失效时间错开。

      private void setRandomTimeForReidsKey(String redisKey,String value){
          //随机函数
          Random rand = new Random();
          //随机获取30分钟内(30*60)的随机数
          int times = rand.nextInt(1800);
          //设置缓存时间(缓存的key,缓存的值,失效时间:单位秒)
          redisClient.setNxEx(redisKey,value,times);
      }
      
    • 这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

三、缓存雪崩

3.1 缓存雪崩是什么

  通常,为了保证 Redis 中的数据与数据库中的数据一致性,通常会给 Redis 里的数据设置过期时间。当缓存数据过期后,用户访问的数据如果不在 Redis 里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。

应用 Redis 数据库 从缓存读取数据 缓存过期 缓存不存在 从数据库读取数据 返回数据库中的数据 将数据加载到缓存 应用 Redis 数据库

  但当Redis 故障宕机或者缓存中大批量的数据同一时间过期(失效),而此时数据访问量又非常大,无法在 Redis 中处理,于是全部直接访问数据库,从而导致数据库压力突然暴增,严重时甚至可能导致数据库崩溃。就像雪崩一样,引发一系列连锁效应,从而波及整个系统崩溃,这种现象被称为缓存雪崩。如下图所示:
在这里插入图片描述

  假设当时每秒6000个请求,本来缓存在可以扛住每秒5000个请求,但是缓存当时所有的Key都失效了。此时1秒6000个请求全部落数据库,数据库必然扛不住,可能DBA都没反应过来就直接挂了,即便是重启数据库,但是数据库立马又被新的流量给打死了。以秒杀系统为例,图示说明:
在这里插入图片描述

  它和缓存击穿不同,缓存击穿是在并发量特别大时,某一个热点 key 突然过期,而缓存雪崩则是大量的 key 同时过期,因此它们根本不是一个量级。

3.2 解决方案

  出现上述情况的常见原因主要有以下两点:

  • 大量缓存数据同时过期,导致本应请求到缓存的需重新从数据库中获取数据。
  • Redis 本身出现故障,无法处理请求,那自然会再请求到数据库那里。

  针对上面出现故障的情况,可以从以下几点出发解决:

  • 事前:构建高可用的集群,实现主 Redis 实例挂掉后,能有其他从库快速切换为主库,继续提供服务,避免全盘崩溃。
  • 事中:在往 Redis 存数据时,可以通过随机、微调、均匀设置等方式设置过期时间,这样可以保证数据不会在同一时间大面积失效。如果事情已经发生了,那就要为了防止数据库被大量的请求搞崩溃,可以采用服务熔断或者请求限流的方法。当然服务熔断相对粗暴一些,停止服务直到redis服务恢复;而请求限流相对温和一些,保证一些请求可以处理,不过还是看具体业务情况选择合适的处理方案。
  • 事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

四、拓展

4.1 缓存预热

  缓存预热就是系统上线前后,将相关的缓存数据直接加载到缓存系统中去,而不依赖用户。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据,这样可以避免那么系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。根据数据不同量级,可以有以下几种做法:

  • 数据量不大:项目启动的时候自动进行加载。
  • 数据量较大:后台定时刷新缓存。
  • 数据量极大:只针对热点数据进行预加载缓存操作。

4.2 缓存降级

  缓存降级是指当缓存失效或缓存服务出现问题时,为了防止缓存服务故障,导致数据库跟着一起发生雪崩问题,所以也不去访问数据库,但因为一些原因,仍然想要保证服务还是基本可用的,虽然肯定会是有损服务。因此,对于不重要的缓存数据,我们可以采取服务降级策略。一般做法有以下两种:

  • 直接访问内存部分的数据缓存。
  • 直接返回系统设置的默认值。

五、结语

  Redis 缓存异常会面临的三个问题:缓存雪崩、击穿和穿透。其中,缓存雪崩和缓存击穿主要原因是数据不在缓存中,而导致大量请求访问了数据库,数据库压力骤增,容易引发一系列连锁反应,导致系统奔溃。不过,一旦数据被重新加载回缓存,应用又可以从缓存快速读取数据,不再继续访问数据库,数据库的压力也会瞬间降下来。因此,缓存雪崩和缓存击穿应对的方案比较类似。而缓存穿透主要原因是数据既不在缓存也不在数据库中。因此,缓存穿透与缓存雪崩、击穿应对的方案不太一样。
  Redis 缓存在互联网中至关重要,可以很大的提升系统效率。 本文介绍的缓存异常以及解决思路有可能不够全面,但也提供相应的解决思路和代码大体实现,希望可以为大家提供一些遇到缓存问题时的解决思路。如果有不足的地方,也请帮忙指出,大家共同进步。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/648842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

常见web安全漏洞

一、信息泄露 概念 信息泄露是由于Web服务器或应用程序没有正确处理一些特殊请求,泄露Web服务器的一些敏感信 息,如用户名、密码、源代码、服务器信息、配置信息等。 造成信息泄露主要的三个原因: ①Web服务器配置存在问题,导致一些系统…

mac 安装java jjdk8 jdk11 jdk17 等

oracle官网 https://www.oracle.com/java/technologies/downloads/ 查看当前电脑是英特尔的x86 还是arm uname -m 选择指定版本,指定平台的安装包: JDK8 JDK11的,需要当前页面往下拉: 下载到的安装包,双击安装&#x…

扭蛋机小程序开发,数字化发展对行业带来的优势

随着科技的不断进步和大众对娱乐消费需求的提高,线上扭蛋机得到了快速发展,市场规模不断扩大。线上扭蛋机是基于淘宝的小程序,它以电商的模式让消费者进行虚拟扭蛋,获得各类商品,扭蛋机小程序中的商品包括玩具、IP周边…

【百度智能体】零代码创建你的 AI 宠物助手

前言 今天给大家介绍一下百度的 AI 产品 – 百度智能体,在文心智能体平台你可以0代码就可以创建出属于自己的 AI 机器人,几乎可以选择任何你想要的领域或者行业机器人,进行无代码打造自己的对话助手,本文将介绍文心智能体&#x…

3D技术的应用领域

3D技术在现代科技和工业中有广泛的应用,其涵盖的领域非常广泛,从娱乐到医学,再到制造业和建筑,3D技术正在改变我们理解和互动的方式。以下是一些主要的应用领域。北京木奇移动技术有限公司,专业的软件外包开发公司&…

单点登录与JWT

JWT:JSON Web Token JWT的作用是用户授权(Authorization),而不是用户的身份认证(Authentication) 授权(Authorization)vs认证(Authentication) 用户认证指的是使用用户名、密码来…

RedHat9 | 配置与管理DNS服务器

一、 知识预备 1、DNS服务器的分类 主DNS服务器 主DNS服务器复制维护所管辖域的域名服务信息,它从域管理员构造的本地磁盘文件中的加载域信息。该文件包含服务器具有管理权的的一部分域结构的精确信息,配置主域服务器需要一整套配置文件: …

瑜伽馆约课会员管理系统小程序的作用是什么

瑜伽馆有着众多学员,如瘦身、改变气质、减脂塑形等往往属于长期多次跟随教练学习,或是自己在家里学习等,对商家来说,品牌宣传、吸引客户到店以及长期经营、提高自身服务效率是重中之重。 客户多次进店享受服务的同时还需要悦己&a…

关于单元测试

关于单元测试的一些总结:

紧固件松动的危害及原因——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 紧固件松动,这一看似微小的机械问题,实际上可能引发一系列严重的后果。在机械设备中,紧固件扮演着至关重要的角色,它们通过紧固作用将各个部件紧密连接在一起,…

学浪视频怎么下载保存到本地

你是否曾经因为想要保存一份珍贵的学浪视频却苦于无法下载而感到烦恼?现在,我将向你揭示一个简单易行的方法,让你轻松地将学浪视频保存到本地,随时随地享受学习的乐趣。你是否曾经因为想要保存一份珍贵的学浪视频却苦于无法下载而…

vue-3d-loader 加载多个模型

需求 1、在使用three.js进行开发的过程中,需要列表加载多个模型,并根据需要多模型进行加载。 2、当鼠标移动到图片上去的时候,开始加载模型, 模型进行加载和展示。 3、在制作3d沉浸式商城时,需要根据需求&#xff0…

vscode+conda,选择虚拟环境下的python解释器提示解释器无效?

问题描述 在开发一个python脚本过程中,试用conda管理虚拟环境,用vscode进行开发,遇到的问题是激活虚拟环境后,在vscode中选择对应环境下的python解释器,提示“选择的解释器无效”。 使用的是Miniconda,虚拟环境下python版本为2.7,vscode的python插件为2023.14.0。 解…

存储方式 - 前端学习

1. cookie是什么?你了解cookie吗? 在计算机领域中,特指一种由服务器发送到用户浏览器并保存在用户计算机上的小型文本文件。这个文件可以被服务器用来识别用户身份、跟踪用户活动、保存用户设置等。它通常由名称、值、域名、路径、过期时间等…

液氢产业化进程提速 液氢装备检测市场需求空间广阔

液氢产业化进程提速 液氢装备检测市场需求空间广阔 液氢装备检测试验项目涉及到火烧试验、置换试验、振动试验、燃烧实验、高压氢循环试验、预冷试验、液氢阀门检测试验等。检测试验是推动氢能技术自主化、高质量发展的重要步骤,近年来,随着液氢应用场景…

C++之std::is_trivially_copyable(平凡可复制类型检测)

目录 1.C基础回顾 1.1.平凡类型 1.2.平凡可复制类型 1.3.标准布局类型 2.std::is_trivially_copyable 2.1.定义 2.2.使用 2.3.总结 1.C基础回顾 在C11中,平凡类型(Trivial Type)、平凡可复制类型(TrivialCopyable&#x…

mysql中单表查询方法

大家好。我们知道,mysql有一个查询优化器的模块。当我们用sql语句查询表中记录时,会对这条查询语句进行语法解析,然后就会交给查询优化器来进行优化,优化后生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查…

机器学习之制作数据集(CPU版本)

文章目录 一、配置环境1.1 Anaconda 下载安装1.1.1 Anaconda 官网下载1.1.2 清华镜像站下载1.1.3 Anaconda 安装 1.2 配置虚拟环境1.4 Paddlepaddle 安装(CPU版本)1.5 PaddleOCR 下载1.6 PPOCRLabel 安装 二、数据集制作2.1 运行 PPOCRLabel2.2 数据标注…

理解I2C总线规范

前言: I2C 总线,也可写作IIC总线(Inter-Integrated Circuit),是一种非常流行且功能强大的总线,用于主设备(或多个主设备)与单个或多个从设备之间的通信。图 1 说明了有多个不同的外…

YOLOV5 改进:替换backbone为EfficientNet

1、介绍 本章将会把yolov5的主干网络替换成EfficientNet V2,这里直接粘贴代码 详细的可以参考之前的内容:YOLOV5 改进:替换backbone(MobileNet为例)_yolov5主干网络更换为mobile-CSDN博客 更多的backbone更换参考本专栏: YOLOV5 实战项目(训练、部署、改进等等)_听风吹…