没遇到过这三个问题都不好意思说用过Redis

2023-11-12

缓存是互联网应用中不可或缺的一部分。而提到缓存,就不得不提它的三个经典问题——缓存穿透、缓存击穿和缓存雪崩,我称它们为缓存问题三兄弟。

缓存的作用主要有两个:一来提升访问速度;二来保护数据库。在业务量不大的时候,通常没什么大问题。但当业务量起来以后,如果缓存使用不合理,三兄弟一定会如约而至,让你体验一下现实的残酷。

三兄弟不来则已,一来轻则影响系统性能,重则直接拖垮数据库,导致系统瘫痪。因此,我们不可掉以轻心,要防患于未然。

缓存穿透

一个请求到达服务器时,正常情况下是按照如下流程进行的。

在这里插入图片描述

即按照如下步骤:

  1. 查询缓存,如果命中则返回
  2. 缓存未命中,则查询数据库
  3. 将从数据库中查询到的数据写入缓存并返回

如果每次都是这样按部就班的处理,倒也相安无事。但是,凡事就怕但是。但是总会有例外,假如请求方对一个(数据库中)根本不存在的数据进行访问,那么按照上面的流程,缓存就形同虚设了。因为不存在,所以不会被写入缓存,这样请求每次都会打到数据库,这个现象就是所谓的「缓存穿透」了。

如果只是因为个别请求去查询不存在的数据,那其实也没什么大事。但缓存穿透通常是伴随一些「恶意请求」而来,通常是在短时间内涌入大量请求。如果放任不管,就等着数据库宕机吧。

如何解决

了解了导致缓存穿透的原因,那么解决方案也就明了了。可以从两个方面下手:

  • 缓存不存在的记录
  • 过滤不存在的请求

啥?不存在的记录咋缓存?其实很简单,如果数据库中也查不到,那就将缓存的 value 设置成 null 即可(注意要根据业务特性设置合理的过期时间)。

过滤不存在的请求,当一个请求到达服务器,比如:

GET /api/user/1

过滤器会先判断该资源是否存在,如果存在则放行,不存在则直接返回,从而起到保护系统的作用。

这种方式也有比较成熟的方案。比如布隆过滤器和布谷鸟过滤器(升级版布隆布隆过滤器)。

双重加固

不管请求不存在的资源是有意还是无意,都不是我们想要的。所以,我们可以设定一个访问频率,一定时间内频繁(超出正常用户的极限)访问,可以对请求方加以限制(如 IP 限制)。另外,一些接口可以加入认证,必须登录才能访问。

缓存击穿

通常情况,我们会为缓存设置一个过期时间。而如果在一个资源的缓存过期以后(或者还未来得及缓存),瞬间涌入大量查询该资源的请求,那么这些请求就都会一股脑的奔向数据库,这时,我们的数据库可就惨了,可能秒秒钟挂掉。这种情况我们称之为缓存击穿。

如何解决

要解决缓存击穿也有两种思路:

  • 永不过期
  • 加锁

先看第一种,短时间内被大量访问的通常是热点资源,针对这类资源我们可以不设置过期时间(永不过期),当资源有变化时通过程序去更新缓存。

再来看第二种,我们可以使用加锁的方式(一般 JVM 级别的锁即可)来避免击穿。当缓存过期之后,进来的请求,先要获得一把锁(也就是去数据库查询的资格),然后再去查询数据库,最后将数据添加到缓存。这样就可以保证同一时刻(一个服务实例)只会有一个请求去查库了,其他线程等缓存有值以后,再去缓存取。

加锁伪代码示例:

 public String getData() throws InterruptedException {
    // 从缓存取值
    String result = getFromCache();
    // 取到直接返回
    if (Objects.nonNull(result)) {
        return result;
    }
    
    // 尝试获取锁
    if (!lock.tryLock()) {
        // 加锁失败则休息一会
        Thread.sleep(10);
        return getData();
    }

    // 加锁成功则去数据库取值
    result = getFromDB();
    // 取回后放入缓存
    setFromCache();
    return result;
}

缓存雪崩

缓存雪崩指的是,缓存中大量的 key 在同一时刻集体过期,导致大量请求涌入到数据库。

有人把缓存服务由于一些原因不可用称为缓存雪崩,我觉得这么叫不太合适。

你想象一下什么是雪崩,大量的雪花集体从山上往下跳就是雪崩。那么对应到缓存的场景,我们可以把 Redis 看做是山,而 Redis 里的 key 就是雪花。Redis 中大量的 key 同时失效,就好比是山上大量的雪花同时往下掉是一样的。所以雪崩用来比喻大量 key 集中失效的情况明显更贴切。而缓存服务挂掉应该属于缓存服务故障,可以采取缓存集群的方式来提高可用性。

如何解决

要解决缓存雪崩的问题,有两种思路:

  • 分散过期时间
  • 永不过期

分散过期时间很容易想到,既然雪崩是因为 key 集体过期导致的,那么把它们过期的时间分散开就可以避免这种问题了。

另一种思路,跟解决缓存击穿一样,将缓存设置为永不过期。

永不过期的方案有一定的局限性,要看具体的业务,不能粗暴的将所有缓存都设置成不过期。

总结

每种技术方案都有其适用的业务场景,也都有其局限性。没有一个方案能够应对所有问题,合适即是好。但从上面的方案中还是能看到一些通用的思想的,比如:尽早返回。咋理解呢?就是让调用链尽量的短,能拦在应用服务之前的绝不放行(布隆过滤);能从缓存取到的绝不再去查库。

更多独家精彩内容尽在我的新书《Spring Boot趣味实战课》中。
请添加图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

没遇到过这三个问题都不好意思说用过Redis 的相关文章

  • Java - 因内存不足错误而关闭

    关于如何最好地处理这个问题 我听到了非常矛盾的事情 并且陷入了以下困境 OOME 会导致一个线程崩溃 但不会导致整个应用程序崩溃 我需要关闭整个应用程序 但不能 因为线程没有剩余内存 我一直认为最佳实践是让它们离开 这样 JVM 就会死掉
  • “java.io.IOException:连接超时”和“SocketTimeoutException:读取超时”之间有什么区别

    如果我设置一个套接字 SoTimeout 并从中读取 当读取时间超过超时限制时 我会收到 SocketTimeoutException 读取超时 这是我的例子中的堆栈 java net SocketTimeoutException Read
  • 如何使用 JAVA 代码以编程方式捕获线程转储?

    我想通过 java 代码生成线程转储 我尝试使用 ThreadMXBean 为此 但我没有以正确的格式获得线程转储 因为我们正在使用jstack命令 请任何人提供一些帮助 他们是否有其他方式获取线程转储 使用任何其他 API 我想要的线程转
  • Logback:SizeAndTimeBasedRollingPolicy 不遵守totalSizeCap

    我正在尝试以一种方式管理我的日志记录 一旦达到总累积大小限制或达到最大历史记录限制 我最旧的存档日志文件就会被删除 当使用SizeAndTimeBasedRollingPolicy在 Logback 1 1 7 中 滚动文件追加器将继续创建
  • 为自定义驱动程序创建 GraphicsDevice

    我正在开发一个在嵌入式系统中使用 Java 的项目 我有用于屏幕和触摸输入的驱动程序 以及用于文本输入的虚拟键盘 我的屏幕驱动程序有一个Graphics2D您可以绘制的对象和repaint Rectangle 更新方法 类似地 触摸驱动器能
  • 为什么 MOVE CURSOR 在 OS X Mountain Lion 上不显示?

    我正在做一个项目 想看看 Swing 提供的每个光标是什么样子的 public class Test public static void main String args JFrame frame new JFrame frame set
  • 添加到列表时有没有办法避免循环?

    我想知道这样的代码 List
  • 虽然我的类已加载,但 Class.forName 抛出 ClassNotFoundException

    代码如下 它的作用是加载我放在主目录中的 jar 文件中的所有类 import java io File import java util jar JarFile import java util jar JarEntry import j
  • 当 minifyEnabled 为 true 时 Android 应用程序崩溃

    我正在使用多模块应用程序 并且该应用程序崩溃时minifyEnabled true in the installed模块的build gradle 以下是从游戏控制台检索到的反混淆堆栈跟踪 FATAL EXCEPTION Controlle
  • Java:如何确定文件所在的驱动器类型?

    Java 是否有一种独立于平台的方法来检测文件所在的驱动器类型 基本上我有兴趣区分 硬盘 可移动驱动器 如 USB 记忆棒 和网络共享 JNI JNA 解决方案不会有帮助 可以假设 Java 7 您可以使用 Java 执行 cmd fsut
  • Spring Data JPA:查询如何返回非实体对象或对象列表?

    我在我的项目中使用 Spring Data JPA 我正在演奏数百万张唱片 我有一个要求 我必须获取各种表的数据并构建一个对象 然后将其绘制在 UI 上 现在如何实现我的 Spring 数据存储库 我读到它可以通过命名本机查询来实现 如果指
  • 寻找局部最小值

    下面的代码正确地找到了数组的局部最大值 但未能找到局部最小值 我已经进行了网络搜索 以找到找到最小值的最佳方法 并且根据这些搜索 我认为我正在使用下面的正确方法 但是 在几天的时间里多次检查每一行之后 下面的代码中有一些我仍然没有看到的错误
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • 如何停止执行的 Jar 文件

    这感觉像是一个愚蠢的问题 但我似乎无法弄清楚 当我在 Windows 上运行 jar 文件时 它不会出现在任务管理器进程中 我怎样才能终止它 我已经尝试过 TASKKILL 但它对我也不起作用 On Linux ps ef grep jav
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • 源值 1.5 的错误已过时,将在未来版本中删除

    我使用 scala maven plugin 来编译包含 scala 和 java 代码的项目 我已经将源和目标设置为1 7 但不知道为什么maven仍然使用1 5 这是我在 pom xml 中的插件
  • Java 的 PriorityQueue 与最小堆有何不同?

    他们为什么命名PriorityQueue如果你不能插入优先级 它看起来与堆非常相似 有什么区别吗 如果没有区别那为什么叫它PriorityQueue而不是堆 默认的PriorityQueue是用Min Heap实现的 即栈顶元素是堆中最小的
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级
  • 将对象从手机共享到 Android Wear

    我创建了一个应用程序 在此应用程序中 您拥有包含 2 个字符串 姓名和年龄 和一个位图 头像 的对象 所有内容都保存到 sqlite 数据库中 现在我希望可以在我的智能手表上访问这些对象 所以我想实现的是你可以去启动 启动应用程序并向左和向

随机推荐

  • got an unexpected keyword argument 'xxx'

    这几天在捣鼓pyecharts的地图功能
  • Windows Mobile 设备中心 for vista 一览

    2007年06月21日 14 30 00 Microsoft Windows Mobile 设备中心 6 1 在6月6日发布了最新版 今天为了能在Vista开发PPC 或Wince设备 程序 下载安装了该程序 启动后界面确实很炫 和媒体中心
  • 【论文合集】2022年11月医学影像期刊论文合集

    本月IEEE Transactions on Medical Imaging 1区 top if 11 037 共41篇 Medical Image Analysis 1区 top if 13 828 共47篇 标题高频词汇 segment
  • Non-terminating decimal expansion; no exact representable decimal result.

    日志信息 问题分析 由于 BigDecimal 是不可变的 任意精度的有符号十进制数 所以可以做精确计算 但在除法中 准确的商可能是一个无限长的十进制扩展 例如 1 除以 3 所得的商 若我们在做除法时 没有指定舍入模式 无法表示为准确的结
  • C# 串口通信 stm32 电机

    前几天已经完成了stm32通过PWM对电机的控制 这几天趁上班之余 也完成了c 通过串口通信控制电机的运行 界面如下 好久没写文章了 发现非常不擅长分享和表达 第一反应是演示出来 可惜这里不能有动画 功能不强大啊 哪天有空了 把上位机代码和
  • json库 nlohmann/json 的基本使用

    C 的json库有很多 但nlohmann 链接 https github com nlohmann json 大概是目前使用最方便的跨平台json库了 其可以让用户以modern C 的方式解析和构建json 性能比rapidjson库略
  • 安卓c语言获取context,Android中Context详解 ---- 你所不知道的Context

    二 什么时候创建Context实例 熟悉了Context的继承关系后 我们接下来分析应用程序在什么情况需要创建Context对象的 应用程序创建Context实例的 情况有如下几种情况 1 创建Application 对象时 而且整个App
  • 一点就通——ChatGPT翻译润色的最新简明使用方案

    prompt使用推荐 1 翻译prompt 翻译主要有两种 第一种是我们的老朋友厦门大学潘王雨昂 个人主页 pwya github io 所编写使用的prompt 第二种是我自己改造的 1 我希望你能担任英语翻译 拼写校对和修辞改进的角色
  • JAVA——JSch

    第 1 章 JSch简介 1 1 简述 1 jsch是ssh2的一个纯Java实现 它允许你连接到一个sshd服务器 使用端口转发 X11转发 文件传输等 2 SSH 是较可靠 专为远程登录会话和其他网络服务提供安全性的协议 3 ftp协议
  • 金融量化— 动量策略(Momentum Strategy)

    什么是动量效应和动量交易策略 动量效应是指过去收益较高的资产 在未来一段时间内仍获得较高的收益 过去收益较低的资产在未来仍获得较低的收益 对于动量效应现象的解释 传统金融学认为 动量效应的存在并不是市场无效的证据 并试图从理性风险补偿这一角
  • Vue中的Diff算法

    Vue中的Diff算法 本篇文章主要介绍Diff算法的思想和Vue中对Diff算法的基本实现 1 为什么要用Diff算法 由于在浏览器中操作DOM的代价是非常 昂贵 的 所以才在Vue引入了Virtual DOM Virtual DOM是对
  • Java Response实现文件下载

    Servlet代码 public class ResponseFileServlet extends HttpServlet public void doGet HttpServletRequest request HttpServletR
  • HTML---表格合并(详解)

    例子详解 关键字解释 border 1 表格标签默认是无边框的 所以这里加上border 1 是为了更好的进行样式展示 colspan 2 表示的是进行单元格列合并 在该行中 先是th表头占据了一个单元格 之后遇到th 用colspan 2
  • JAVA错误: 在类中找不到主方法, 请将主方法定义为:public static void main(String[] args)

    一个类只有包含了入口才能运行 这个入口告诉了系统从哪开始运行程序 入口的格式固定 例如 class test public static void main String args 入口代码
  • 拷贝粘贴踩坑之Content-length

    最近在开发时遇到需要上传文件的功能 我自然而然地上网找了段实现上传功能的代码 我的程序有两个地方需要上传文件 这段代码在一处能用 另一处就报错了 代码如下 File file new File 文件路径 MultipartEntityBui
  • linux与windows文件共享及全屏

    1 安装VMware tools 虚拟机选项 安装VMware tools 然后在Ubuntu系统中弹出的VMware tools窗口中 找到VMwaretools 9 6 0 1294478 tar gz 复制到桌面 然后在桌面上的VMw
  • [python]windos下打包一个简单的python脚本

    1 脚本一览 python脚本如下 结构比较简单 基本功能是根据已有的名单去统计群里面没有参加接龙的人员和人数 脚本只涉及到python自带的库 并且在运行时需要读取同目录下的两个txt文件 最后打印出没有统计结果 import os st
  • 微信营销系统如何使用效果会更好

    微信作为中国最大的社交平台之一 已经成为企业私域营销的重要阵地 在这个庞大的社交网络中 如何使用微信营销系统 将直接影响到企业的营销效果 本文将深入探讨如何更好地利用微信营销系统 以实现更好的私域营销效果 1 确定营销目标和策略 在使用微信
  • Java手写ArrayList和拓展案例

    Java手写ArrayList 思维导图 mermaid svg ncipf5YgQSnFk37I font family trebuchet ms verdana arial sans serif font size 16px fill
  • 没遇到过这三个问题都不好意思说用过Redis

    缓存是互联网应用中不可或缺的一部分 而提到缓存 就不得不提它的三个经典问题 缓存穿透 缓存击穿和缓存雪崩 我称它们为缓存问题三兄弟 缓存的作用主要有两个 一来提升访问速度 二来保护数据库 在业务量不大的时候 通常没什么大问题 但当业务量起来