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

news2024/10/6 16:20:57

在这里插入图片描述

文章目录

  • 概述
  • 一、缓存穿透
    • 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 缓存在互联网中至关重要,可以很大的提升系统效率。 本文介绍的缓存异常以及解决思路有可能不够全面,但也提供相应的解决思路和代码大体实现,希望可以为大家提供一些遇到缓存问题时的解决思路。如果有不足的地方,也请帮忙指出,大家共同进步。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【漫画算法】哈希表:古代皇帝的秘密魔法书

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容,和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣! 推荐:数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航: LeetCode解锁100…

如何确保大模型 RAG 生成的信息是基于可靠的数据源?

在不断发展的人工智能 (AI) 领域中,检索增强生成 (RAG) 已成为一种强大的技术。 RAG 弥合了大型语言模型 (LLM) 与外部知识源之间的差距,使 AI 系统能够提供更全面和信息丰富的响应。然而,一个关键因素有时会缺失——透明性。 我们如何能够…

Android窗口管理

一 概述 本篇文章主要讲 Window、WindowManager、WindowManagerService 三者之间的关系及其运行机制。总的来说 Window 表示的是一种抽象的功能集合,具体实现为 PhoneWindow。WindowManager 是外界访问 Window 的入口,对 Window 的访问必须通过 Window…

状态转换图

根据本章开头讲的结构化分析的第3条准则,在需求分析过程中应该建立起软件系统的行为模型。状态转换图(简称为状态图)通过描绘系统的状态及引起系统状态转换的事件,来表示系统的行为。此外,状态图还指明了作为特定事件的结果系统将做哪些动作(例如,处理数据)。因此,状态图提供了…

【轻松压缩,一键搞定】您的图片处理神器来了!

压图地址 一个功能强大的图片处理工具,它可以满足用户对于图片压缩、格式转换、质量调节以及长图片分割等多种需求。 【轻松压缩,一键搞定】您的图片处理神器来了! 🎉 您是否曾为图片太大无法上传而烦恼?是否为图片格…

rk3568_atomic

文章目录 前言一、atomic是什么?二、原子操作API函数1.atomic原子操作2.原子位操作API三、atomic驱动实验总结前言 本文记录的是正点原子rk3568开发板的atomic实验 一、atomic是什么? 不同的线程在进行读写的过程中,可能会冲突乱入,导致会有预想不到的结果。所以为了让数…

信息系统项目管理师0131:输出(8项目整合管理—8.7监控项目工作—8.7.3输出)

点击查看专栏目录 文章目录 8.7.3 输出8.7.3 输出 工作绩效报告工作绩效信息可以用实体或电子形式加以合并、记录和分发。基于工作绩效信息,以实体或电子形式编制形成工作绩效报告,以制定决策、采取行动或引起关注。根据项目沟通管理计划,通过沟通过程向项目干系人发送工作绩…

文档档案管理系统整体建设方案书(实际项目原件word2024)

1.系统概述 1.1.需求描述 1.2.需求分析 1.3.重难点分析 1.4.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 数据备份技术 3.系统功能设计 3.1.功能清单列表 3.2.基础数据管理 3.3.位置管理 3.4.文档使用 3.5.文档管理 软件全套资料包获取方式①:软件项…

[windows系统安装/重装系统][step-4][番外篇-2]N卡驱动重装 |解决:开机几小时后电脑卡顿 | 后台自动运行了上千个Rundll32进程问题

现象 开机几小时后,电脑变卡,打开后台管理器都卡,后台管理去转圈圈一小会儿后看到后台进程上千个,好多个Rundll32进程 重启下运行会稍快 重启后运行快,后台管理器反应也快 打开后台管理器不卡(几小时后打…

Hadoop3:HDFS中DataNode与NameNode的工作流程

一、DataNode中的数据情况 数据位置 /opt/module/hadoop-3.1.3/data/dfs/data/current/BP-823420375-192.168.31.102-1714395693863/current/finalized/subdir0/subdir0块信息 每个块信息,由两个文件保存,xxx.meta保存的是数据长度、校验和、时间戳&am…

基于Java、SpringBoot和uniapp在线考试系统安卓APP和微信小程序

摘要 基于Java、SpringBoot和uniapp的在线考试系统安卓APP微信小程序是一种结合了现代Web开发技术和移动应用技术的解决方案,旨在为教育机构提供一个方便、高效和灵活的在线考试平台。该系统采用Java语言进行后端开发,使用SpringBoot框架简化企业级应用…

netmask一键修改子网掩码(KALI工具系列八)

目录 1、KALI LINUX简介 2、netmask工具简介 3、在KALI中使用netmask 3.1 目标主机IP(win) 3.2 KALI的IP 4、命令示例 4.1 查看版本 4.2 修改etho的子网掩码 4.3 查看状态信息 4.4 查看子网掩码 4.5 查看范围 4.6 DNS查看 5.、总结 1、KAL…

【实际项目精选源码】ehr人力资源管理系统实现案例(java,vue)

一、项目介绍 一款全源码可二开,可基于云部署、私有部署的企业级数字化人力资源管理系统,涵盖了招聘、人事、考勤、绩效、社保、酬薪六大模块,解决了从人事招聘到酬薪计算的全周期人力资源管理,符合当下大中小型企业组织架构管理运…

Redis可视化工具:Another Redis Desktop Manager下载安装使用

1.Github下载 github下载地址: Releases qishibo/AnotherRedisDesktopManager GitHub 2. 安装 直接双击exe文件进行安装 3. 连接Redis服务 先启动Redis服务,具体启动过程可参考: Windows安装并启动Redis服务端(zip包&#xff09…

Linux -- 进程间通信的五种方式

IPC(InterProcess Communication)的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Stream支持不同主机上的两个进程IPC。 管道(Pipes)&#xff1a…

618快到了,送大家一款自动化脚本工具,一起薅羊毛

前言 一年一次的618活动来了,大家做好准备了,奇谈君为大家准备好用的618神器,解放双手,简单操作就可以把红包拿到手。 京淘自动助手 首次使用前需要进行设置 将手机的无障碍权限和悬浮窗权限打开 设置完成后,可以把…

编程实战:类C语法的编译型脚本解释器(四)总入口和使用方法

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 系列入口: 编程实战…

深入了解数据库设计中的规范化与反规范化

目录 零、前言 一、一些基本术语 二、关系模式 2.1. 什么是关系模式 2.2. 示例 三、数据依赖 3.1. 函数依赖 3.1.1. 完全函数依赖 3.1.2. 部分函数依赖 3.1.3. 传递函数依赖 3.2. 多值依赖 3.3. 连接依赖 四、规范化 4.1. 第一范式(1NF) …

Redis 性能管理

一、Redis 性能管理 #查看Redis内存使用 172.168.1.11:6379> info memory 1. 内存碎片率 操作系统分配的内存值 used_memory_rss 除以 Redis 使用的内存总量值 used_memory 计算得出。内存值 used_memory_rss 表示该进程所占物理内存的大小,即为操作系统分配给…

批量化处理和矩阵(torch)

左边是权重,右边是变量 高维可以看成二维的堆叠 总结:二维是一维的堆叠,三维是二维的堆叠。但似乎是为了引入矩阵,本来应该是左上角是第一组权重和第一组变量的乘积这种表示表示来着,最后成了和列向量乘积&#xff…