深度剖析 Redis:缓存穿透、击穿与雪崩问题及实战解决方案

news2025/2/8 6:16:35

一、缓存基本使用逻辑

在应用程序中,为了提高数据访问效率,常常会使用缓存。一般的缓存使用逻辑是:根据 key 去 Redis 查询是否有数据,如果命中就直接返回缓存中的数据;如果缓存不存在,则查询数据库,若数据库存在数据则把数据存储到 Redis 中并返回。

以下是简单的 Java 代码示例:

import redis.clients.jedis.Jedis;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class CacheExample {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;
    private static final String DB_URL = "jdbc:mysql://localhost:3306/yourdb";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "password";

    public String getData(String key) {
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
        String data = jedis.get(key);
        if (data != null) {
            jedis.close();
            return data;
        }
        try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
             Statement stmt = conn.createStatement()) {
            String sql = "SELECT data FROM your_table WHERE key = '" + key + "'";
            ResultSet rs = stmt.executeQuery(sql);
            if (rs.next()) {
                data = rs.getString("data");
                jedis.set(key, data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        jedis.close();
        return data;
    }
}

二、常见缓存问题及解决方案

(一)缓存穿透

缓存穿透是指查询一个不存在的数据,由于数据库查询不到数据,就会导致每次请求都查数据库。

1. 解决方案一:缓存空数据

查询返回的数据为空,仍然把空结果进行缓存,将过期时间设置为较短的时间。

  • 优点:简单。
  • 缺点:消耗内存,可能会发生不一致的问题。
2. 解决方案二:使用布隆过滤器

 什么是布隆过滤器?

bitmap(位图):相当于是一个以(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1

布隆过滤器作用:布隆过滤器可以用于检索一个元素是否在一个集合中。

布隆过滤器是一种空间效率极高的概率型数据结构,它在判断数据存在性时,存在一定的局限性。由于不同数据经过哈希函数计算后,其哈希值有可能相同,也就是存在哈希冲突,这就导致了布隆过滤器会出现误判的情况。当它判断某个数据存在时,实际上该数据可能并不存在;但当它判断数据不存在时,那就肯定不存在,因为相同的数据计算出的哈希值必然是相同的。此外,布隆过滤器还有一个特性,即无法直接删除数据。因为删除操作可能会影响其他数据的判断结果,导致更多的误判。

实际应用:

查询缓存前,先查询布隆过滤器是否存在这个 key,如果存在再查询 Redis。Redis 4.0 后,可以直接安装 RedisBloom,然后 Java 集成 Redisson 即可实现。

  • 优点:内存占用较少,不会产生多余的 key
  • 缺点:实现稍微复杂,存在误判,不能删除 key
3. 综合应用

在实际应用中,选择哪种方案解决缓存穿透问题需要综合多方面因素考虑,以下是具体情况分析:

  • 如果业务场景对数据准确性要求极高、内存资源充足且数据量相对较小:可以优先考虑缓存空数据方案。比如一些小型的、对数据一致性要求严格的内部管理系统,偶尔出现少量不存在的数据查询,缓存空数据既能快速响应,又不会对内存造成太大压力,实现起来也简单,能有效避免缓存穿透对数据库的冲击。

  • 如果业务场景数据量巨大、对内存占用敏感且允许一定程度的误判:使用布隆过滤器更为合适。像大型的电商平台、社交媒体平台等,每天会有海量的数据和高并发的查询请求,布隆过滤器能以较小的内存占用处理大量数据的存在性判断,大大减少了无效的数据库查询,提升系统整体性能。虽然存在误判,但只要误判率在可接受范围内,对整体业务影响不大。而且对于不允许删除 key 的问题,如果业务本身不存在频繁删除数据的操作,或者可以通过其他方式解决数据删除带来的影响,那么布隆过滤器就是一个很好的选择。

实际应用中还可以根据具体情况将两种方案结合使用,比如对于一些热点数据或者重要数据采用缓存空数据方案,保证数据的准确性和一致性;对于大量的非关键数据则使用布隆过滤器进行快速过滤,减少数据库查询压力,以达到更好的效果。

(二)缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,甚至导致数据库崩溃。

1. 互斥锁方案
  • 原理:在缓存失效的瞬间,只允许一个线程去查询数据库并更新缓存,其他线程等待。当这个线程将数据更新到缓存后,其他线程再从缓存中获取数据。
  • 实现方式:可以使用分布式锁(如 Redis 的 SETNX 命令)或者 Java 的 ReentrantLock 等锁机制来实现。在查询缓存前,先尝试获取锁,获取成功的线程去查询数据库和更新缓存,获取失败的线程则休眠一段时间后重新尝试查询缓存。
  • 优点:实现相对简单,能有效避免大量并发请求直接打到数据库。
  • 缺点:存在锁竞争,可能会影响系统的并发性能;如果获取锁的线程出现异常,可能导致其他线程一直等待。
2. 热点数据永不过期
  • 原理:对于一些热点数据,不设置过期时间,使其一直存在于缓存中,这样就不会出现缓存击穿的问题。
  • 实现方式:在将数据存入缓存时,不设置过期时间参数。或者使用定时任务定期更新缓存中的热点数据,保证数据的实时性。
  • 优点:能彻底避免缓存击穿问题,对于热点数据的访问性能非常高。
  • 缺点:会一直占用缓存空间,可能导致缓存内存不足;数据实时性较差,需要通过其他机制来保证数据的更新。
3. 逻辑过期
  • 原理:在缓存数据中添加一个逻辑过期时间字段,而不是依赖缓存的真正过期机制。当业务线程查询到数据的逻辑过期时间已到,但缓存还未过期时,启动一个异步线程去更新缓存数据。
  • 实现方式:在缓存数据结构中增加一个字段用于存储逻辑过期时间。业务线程查询缓存时,判断逻辑过期时间是否已到,如果已到则返回数据,并异步更新缓存。
  • 优点:避免了大量并发请求在缓存过期瞬间同时访问数据库,能保证数据有一定的实时性,同时对业务线程的影响较小。
  • 缺点:需要额外维护逻辑过期时间字段,实现相对复杂;如果异步更新线程出现问题,可能导致数据更新不及时。
4. 二级缓存
  • 原理:使用两层缓存,如一级缓存使用内存缓存(如 Guava Cache),二级缓存使用分布式缓存(如 Redis)。一级缓存的过期时间较短,二级缓存的过期时间较长。当一级缓存失效时,先从二级缓存获取数据,如果二级缓存也失效,则查询数据库并同时更新一级缓存和二级缓存。
  • 实现方式:在应用程序中集成内存缓存框架和分布式缓存客户端,分别设置不同的过期时间和缓存策略。
  • 优点:通过两层缓存的设计,进一步降低了访问数据库的频率,提高了系统的性能和稳定性。
  • 缺点:需要维护两层缓存,增加了系统的复杂性和维护成本;可能存在数据在两层缓存中不一致的情况,需要进行额外的处理。
5. 缓存预热
  • 原理:在系统启动或上线前,提前将一些热点数据加载到缓存中,让缓存中的数据在初始阶段就处于完整状态,避免在系统运行过程中由于缓存未命中而导致缓存击穿。
  • 实现方式:可以通过编写初始化脚本或定时任务,在系统启动时或定时从数据库中查询热点数据并放入缓存。
  • 优点:能有效减少系统运行初期的缓存击穿问题,提高系统的启动速度和初始性能。
  • 缺点:需要提前确定热点数据,对于动态变化的热点数据可能不太适用;如果热点数据量较大,缓存预热的时间可能较长。

(三)缓存雪崩

缓存雪崩是指在缓存系统中,大量的缓存数据在同一时间点过期或失效,再或者是 Redis 宕机,导致大量原本应该从缓存获取数据的请求瞬间都转向数据库,从而使数据库承受巨大的并发压力,甚至可能导致数据库系统崩溃,进而影响整个系统的正常运行。

1. 缓存数据过期时间打散
  • 原理:避免大量缓存数据在同一时间过期,通过给不同的缓存数据设置不同的、随机的过期时间,让缓存数据的过期时间分散开来,降低同一时刻大量缓存失效的可能性。
  • 实现方式:在设置缓存数据的过期时间时,不使用固定的过期时长,而是在一个基础过期时间上加上一个随机的时间偏移量。例如,基础过期时间为 30 分钟,随机偏移量为 0 到 10 分钟之间的随机数,那么最终的过期时间就是 30 到 40 分钟之间的某个值。
  • 优点:实现相对简单,能有效分散缓存过期时间,减少缓存雪崩的风险。
  • 缺点:可能会导致部分数据在较短时间内就过期,需要根据业务情况合理调整随机范围。
2. 多级缓存架构
  • 原理:采用多种不同类型或不同层级的缓存组合,如内存缓存(如 Ehcache、Guava Cache)和分布式缓存(如 Redis)结合,甚至可以再加上本地磁盘缓存等。当一级缓存失效时,尝试从其他级别的缓存中获取数据,减少对数据库的直接访问。
  • 实现方式:将经常访问且对实时性要求较高的数据放在内存缓存中,将相对不那么实时但数据量较大的数据放在分布式缓存中,对于一些历史数据或可以允许一定延迟的数据可以放在本地磁盘缓存中。当内存缓存失效时,先查询分布式缓存,若分布式缓存也失效,再查询本地磁盘缓存或数据库。
  • 优点:通过多级缓存的互补,提高了缓存的命中率和系统的稳定性,降低了数据库的压力。
  • 缺点:增加了系统的复杂性,需要维护多个缓存之间的一致性和数据同步。
3. 数据持久化
  • 原理:将缓存中的数据持久化到硬盘或其他存储介质上,当缓存服务器重启或出现故障导致缓存数据丢失时,可以从持久化存储中快速恢复数据,而不是直接去查询数据库。
  • 实现方式:使用支持数据持久化的缓存系统,如 Redis 的 RDB(Redis Database Backup)和 AOF(Append Only File)持久化机制。RDB 可以定期将内存中的数据快照保存到磁盘上,AOF 则是将所有的写命令追加到文件中,通过重放这些命令来恢复数据。
  • 优点:能在缓存故障时快速恢复数据,减少缓存重建的时间和对数据库的压力。
  • 缺点:需要额外的存储资源来保存持久化数据,并且数据恢复过程可能会有一定的时间开销。
4. 缓存预热
  • 原理:在系统启动或上线前,提前将一些热点数据和基础数据加载到缓存中,确保缓存中有足够的数据来处理初始的请求,避免在系统刚启动时由于缓存为空而导致大量请求直接打到数据库。
  • 实现方式:编写专门的缓存预热脚本或任务,在系统启动时从数据库或其他数据源中查询需要预热的数据,并将其放入缓存。也可以根据业务规则和数据访问频率,定期执行缓存预热操作,更新缓存中的数据。
  • 优点:可以有效减少系统启动时的缓存雪崩风险,提高系统的初始响应速度。
  • 缺点:需要提前确定预热的数据范围和策略,对于动态变化的数据可能需要不断调整预热逻辑。
5. 限流与降级
  • 原理:限流是指限制单位时间内进入系统的请求数量,当请求量超过一定阈值时,拒绝部分请求或返回默认数据,避免大量请求同时访问数据库。降级是指当系统出现压力或故障时,主动关闭一些非核心业务或降低某些业务的服务质量,以保证核心业务的正常运行。
  • 实现方式:可以使用限流框架(如 Guava 的 RateLimiter、Sentinel 等,或者 Nginx、Spring Cloud Gateway)来实现限流功能,根据系统的承受能力设置合适的限流阈值。对于降级,可以在代码中设置降级策略,当满足一定条件时(如缓存大量失效、数据库响应时间过长等),执行降级逻辑,返回默认数据或提示信息。
  • 优点:能在系统出现异常时保护数据库和核心业务,避免系统崩溃,提高系统的稳定性和可靠性。
  • 缺点:可能会影响部分用户的体验,需要合理设置限流和降级策略,尽量减少对业务的影响。

三、总结

在使用缓存的过程中,缓存穿透、缓存击穿和缓存雪崩是常见的问题,每种问题都有其对应的解决方案。在实际应用中,我们需要根据业务的具体需求、数据特点和系统架构等多方面因素,综合选择合适的解决方案,以提高系统的性能和稳定性,避免因缓存问题导致数据库压力过大甚至系统崩溃的情况发生。同时,还需要对缓存系统进行持续的监控和优化,确保其始终处于良好的运行状态。

希望通过本文的介绍,大家能对缓存的使用以及常见问题的解决有更深入的理解,在实际项目中更好地运用缓存技术。

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

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

相关文章

如何使用el-table的多选框

对el-table再次封装,使得功能更加强大! 本人在使用el-table时,因为用到分页,导致上一页勾选的数据在再次返回时,没有选中,故在原有el-table组件的基础之上再次进行了封装。 1.首先让某些不需要勾选的列表进…

【工具变量】上市公司企业渐进式创新程度及渐进式创新锁定数据(1991-2023年)

测算方式: 参考顶刊《经济研究》孙雅慧(2024)老师的做法,用当期创新和往期创新的内容重叠度作为衡量渐进式创新程度的合理指标。通过搜集海量专利摘要,测算当前专利申请和既有专利的内容相似度,反映企业在…

LM Studio 部署本地大语言模型

一、下载安装 1.搜索:lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理,否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…

嵌入式工程师面试经验分享与案例解析

嵌入式工程师岗位受到众多求职者的关注。面试流程严格,技术要求全面,涵盖C/C编程、数据结构与算法、操作系统、嵌入式系统开发、硬件驱动等多个方向。本文将结合真实案例,深入剖析嵌入式工程师的面试流程、常见问题及应对策略,帮助…

英特尔至强服务器CPU销量创14年新低,AMD取得进展

过去几年是英特尔56年历史上最艰难的时期之一。该公司在晶圆代工、消费级处理器和服务器芯片等各个领域都面临困境。随着英特尔重组其晶圆代工业务,新的分析显示其服务器业务的现状和未来前景不容乐观。 英特尔最近发布的10-K文件显示:“数据中心和人工…

判断您的Mac当前使用的是Zsh还是Bash:echo $SHELL、echo $0

要判断您的Mac当前使用的是Zsh还是Bash,可以使用以下方法: 查看默认Shell: 打开“终端”应用程序,然后输入以下命令: echo $SHELL这将显示当前默认使用的Shell。例如,如果输出是/bin/zsh,则说明您使用的是Z…

使用Springboot实现MQTT通信

目录 一、MQ协议 MQTT 特点 MQTT 工作原理 MQTT 主要应用场景 MQTT 配置与注意事项 二、MQTT服务器搭建 三、参考案例 MQTT(Message Queuing Telemetry Transport)是一种基于发布/订阅模型的轻量级消息传输协议,常用于物联网&#xff…

植物大战僵尸融合版(电脑/安卓)

《植物大战僵尸融合版》是一款由B站UP主“蓝飘飘fly”制作的同人策略塔防游戏,基于经典《植物大战僵尸》玩法,加入了独特的植物融合系统。 出于方便,软件是便携版,解压后双击即可畅玩。 游戏主页依旧是植物大战僵尸经典界面。右下…

02DevOps基础环境准备

准备两台Linux的操作系统,最简单的方式就是在本机上使用虚拟机搭建两个操作系统(实际生产环境是两台服务器,虚拟机的方式用于学习使用) 我搭建的两台服务器的ip分别是192.168.1.10、192.168.1.11 192.168.1.10服务器用于安装doc…

苍穹外卖-day12(工作台、数据导出)

工作台Apache POI导出运营数据Excel报表 功能实现:工作台、数据导出 工作台效果图: 数据导出效果图: 在数据统计页面点击数据导出:生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原型 工作台是系统运营的数据看板&…

详解享元模式

引言 在计算机中,内存是非常宝贵的资源,而程序中可能会有大量相似或相同的对象,它们的存在浪费了许多空间。而享元模式通过共享这些对象,从而解决这种问题的。 1.概念 享元模式(Flyweight Pattern):运用共享技术有效地…

openEuler22.03LTS系统升级docker至26.1.4以支持启用ip6tables功能

本文记录了openEuler22.03LTS将docker升级由18.09.0升级至26.1.4的过程(当前docker最新版本为27.5.1,生产环境为保障稳定性,选择升级到上一个大版本26的最新小版本)。 一、现有环境 1、系统版本 [rootlocalhost opt]# cat /etc…

< OS 有关 > Ubuntu 版本升级 实践 24.04 -> 24.10, 安装 .NET

原因: 想安装 .NET 9 去编译 GitHut 项目,这回用不熟悉的 Ubuntu来做,不知道怎么拐去给 Ubuntu 升级,看到现在版本是 24.10 但不是 LTS 版本,记录下升级过程。 一、实践过程: 1. 查看当前版本 命令1: l…

某咨询大数据解决方案介绍(32页PPT)

本文档介绍了一个大数据平台解决方案,旨在解决企业当前面临的数据问题,包括数据定义缺失、重复采集和存储、数据不完整以及缺乏可靠决策依据等。通过引入大数据技术,该方案强调从被动的IT支撑向主动的数据核心服务转型,以实现科学…

matlab simulink 汽车四分之一模型主动被动悬架-LQR

1、内容简介 略 matlab simulink 可以交流、咨询、答疑 124- 2、内容说明 略汽车悬架系统由弹性元件、导向元件和减振器组成,是车身与车轴之间连接的所有组合体零件的总称,也是车架(或承载式车身)与车桥(或车轮)之间一切力传递装置的总称,其主要功能是使车轮与地面有很好的…

从零开始:OpenCV 图像处理快速入门教程

文章大纲 第1章 OpenCV 概述 1.1 OpenCV的模块与功能  1.2 OpenCV的发展 1.3 OpenCV的应用 第2章 基本数据类型 2.1 cv::Vec类 2.2 cv::Point类 2.3 cv::Rng类 2.4 cv::Size类 2.5 cv:&…

强化学习笔记6——异同策略、AC、等其他模型总结

异步两种方法:1:经验回放 2:数据动作非同时产生 举例QLearning为什么是异策略? 生成动作时e的概率从Q表选,1-e概况随机。 更新策略时,贪心策略选择Q_max作为动作。 策略优化两种主要方法:基于梯…

Linux提权--passwd提权

passwd​ 命令用于更改用户密码。在 Linux 系统中,普通用户可以通过 passwd​ 更改自己的密码,但如果攻击者能够以某种方式执行 passwd​ 命令更改 root 用户的密码,他们就能获取 root 权限。 1.常见的 passwd 提权方法 SUID 设置&#xff1…

一、本地部署安装 DeepSeek 并训练本地知识库,并调用对话框进行问答

本地部署安装 DeepSeek 1、硬件环境 操作系统:Windows10 内存:16G 显卡:NIVIDIA GeForce RTX 2060 6G 2、安装步骤 (1)安装 Ollama 访问Ollama 官网,点击 “Download for Windows” 下载安装程序。下载…

海思的一站式集成环境Hispark Studio更新了

HiSpark Studio是海思提供的面向智能设备开发者提供一站式集成开发环境,支持代码编辑、编译、烧录和调试等功能。我以前在评测星闪芯片的时候用过,当时写了篇博客:【星闪开发连载】WS63E开发板Windows环境的构建_hispark studio-CSDN博客。那…