【Java面试】redis雪崩、穿透和击穿详解

一 Redis雪崩、穿透和击穿

1. Redis雪崩
 Redis雪崩是指在某一时刻,缓存中大量的缓存数据同时失效或过期,导致大量的请求直接打到后端数据库,导致数据库负载剧增,引发性能问题甚至崩溃。这通常是因为缓存数据的过期时间设置过于集中,或者在同一时间段内大量缓存同时失效造成的。

2. Redis穿透
 Redis穿透是指恶意或者异常请求查询一个不存在于缓存和数据库中的数据,导致每次请求都会直接访问数据库,增加了数据库负担。这可能是攻击者故意进行的,也可能是由于业务逻辑问题造成的。

3. Redis击穿
 Redis击穿是指某个热点数据突然失效或被删除,而此时大量请求正好同时访问该热点数据,导致这些请求都直接打到数据库上,导致数据库压力激增。与雪崩不同,击穿是因为某个特定的缓存数据失效导致。

示例:

让我们以一个简单的Java代码示例来说明Redis雪崩、穿透和击穿的概念。

假设有一个电影信息查询系统,用户可以根据电影ID查询电影信息。我们使用Redis作为缓存来存储电影信息,但是只对热门电影设置了缓存,其他电影没有被缓存。

@Service
public class MovieService {

    @Autowired
    private MovieRepository movieRepository;

    @Autowired
    private Jedis jedis;

    public Movie getMovieInfo(String movieId) {
        String cacheKey = "movie:" + movieId;
        String cachedInfo = jedis.get(cacheKey);

        if (cachedInfo == null) {
            Movie movie = movieRepository.findById(movieId);
            if (movie != null) {
                jedis.setex(cacheKey, 3600, movie.toString()); // 缓存1小时
                return movie;
            }
        }
        return Movie.fromString(cachedInfo);
    }
}

Redis雪崩示例:
假设在某一时刻,缓存中存储了很多电影信息,这些缓存在同一时间内同时失效,导致大量请求直接访问数据库,造成数据库压力激增。

Redis穿透示例:
有一个恶意用户不断发送不存在的电影ID,每次请求都会绕过缓存,直接查询数据库,导致数据库压力增加。

Redis击穿示例:
假设某个热门电影的缓存在某个时间点失效,而在这个时间点正好有大量用户同时查询该电影信息,导致所有请求直接访问数据库,造成数据库压力激增。

二 解决方案


2.1 对缓存数据的过期时间进行随机化,避免集中失效。

  1. 选择随机时间范围: 首先,你需要选择一个适当的随机时间范围,用于分散缓存数据的过期时间。例如,你可以选择在原始过期时间基础上添加一个随机的秒数,这样每个缓存项的过期时间就会稍微有所不同。

  2. 生成随机时间: 在获取缓存数据时,生成一个随机的秒数,然后将其添加到原始过期时间上,得到一个新的过期时间。

  3. 设置缓存数据: 将缓存数据存储到Redis中,并设置使用上一步生成的新过期时间。
     

    @Service
    public class CacheService {
    
        @Autowired
        private Jedis jedis;
    
        public String getCachedData(String key) {
            String cachedData = jedis.get(key);
    
            if (cachedData == null) {
                // 查询数据库获取数据
                String dbData = Database.queryData(key);
    
                if (dbData != null) {
                    // 生成随机的过期时间(在1小时基础上随机增加0-300秒)
                    int originalExpireTime = 3600; // 1小时的秒数
                    int randomSeconds = new Random().nextInt(300); // 0到300秒的随机数
                    int cacheDuration = originalExpireTime + randomSeconds;
    
                    // 将数据存储到缓存并设置随机过期时间
                    jedis.setex(key, cacheDuration, dbData);
    
                    return dbData;
                }
            }
    
            return cachedData;
        }
    }
    

2.2 使用布隆过滤器来过滤恶意请求,防止缓存穿透。

使用布隆过滤器来过滤恶意请求,以防止缓存穿透是一种常见的防御策略。布隆过滤器是一种数据结构,用于判断一个元素是否存在于集合中,它可以高效地进行快速查询,但可能会有一定的误判率。

下面是一个使用Spring Boot和布隆过滤器来防止缓存穿透的详细举例:

步骤:

引入依赖: 在Spring Boot项目中,添加所需的依赖,包括Spring Boot、Jedis和Google Guava(用于实现布隆过滤器)。

初始化布隆过滤器: 在启动时初始化一个布隆过滤器,用于存储已查询的缓存键。

查询缓存数据: 在获取数据之前,首先检查布隆过滤器,如果缓存键可能存在,则再查询缓存。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import redis.clients.jedis.Jedis;

@Service
public class CacheService {

    private final Jedis jedis;
    private final BloomFilter<String> bloomFilter;

    @Autowired
    public CacheService(Jedis jedis) {
        this.jedis = jedis;
        this.bloomFilter = BloomFilter.create(Funnels.stringFunnel(), 1000, 0.01); // 初始化布隆过滤器
    }

    public String getCachedData(String key) {
        if (!bloomFilter.mightContain(key)) { // 判断是否可能存在于集合中
            return null; // 不再查询缓存和数据库,直接返回null
        }

        String cachedData = jedis.get(key);

        if (cachedData == null) {
            // 查询数据库获取数据
            String dbData = Database.queryData(key);

            if (dbData != null) {
                jedis.setex(key, 3600, dbData); // 缓存1小时
                bloomFilter.put(key); // 将键添加到布隆过滤器中
                return dbData;
            }
        }

        return cachedData;
    }
}

2.3 使用互斥锁(例如分布式锁)来防止击穿,只允许一个请求去查询数据库,其他请求等待或直接使用缓存。

使用互斥锁(分布式锁)来防止击穿是一种常见的策略,可以确保在缓存失效的情况下,只有一个请求能够去查询数据库,其他请求需要等待该请求完成或直接使用缓存。下面是一个使用Spring Boot和Jedis实现分布式锁来防止击穿的代码示例:

步骤:

1、引入依赖: 在Spring Boot项目中,添加所需的依赖,包括Spring Boot和Jedis。

2、获取分布式锁: 在查询数据库之前,使用分布式锁来确保只有一个请求能够进行数据库查询。

3、释放分布式锁: 在查询完成后,释放分布式锁,让其他请求能够继续执行


@Service
public class CacheService {

    @Autowired
    private Jedis jedis;

    public String getCachedData(String key) {
        String cachedData = jedis.get(key);

        if (cachedData == null) {
            // 尝试获取分布式锁,设置锁的过期时间,防止死锁
            String lockKey = "lock:" + key;
            String lockValue = "lockValue";
            SetParams params = new SetParams().ex(60).nx(); // 设置60秒过期时间,只有不存在时才设置

            String acquiredLock = jedis.set(lockKey, lockValue, params);

            if (acquiredLock != null) {
                try {
                    // 查询数据库获取数据
                    String dbData = Database.queryData(key);

                    if (dbData != null) {
                        jedis.setex(key, 3600, dbData); // 缓存1小时
                        return dbData;
                    }
                } finally {
                    // 释放分布式锁
                    jedis.del(lockKey);
                }
            } else {
                // 等待一段时间后重新查询缓存
                try {
                    Thread.sleep(200); // 可以根据实际情况调整等待时间
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                // 重新查询缓存
                cachedData = jedis.get(key);
            }
        }

        return cachedData;
    }
}


2.4 合理设置缓存策略,确保热门数据始终保持缓存,避免缓存雪崩。

确保热门数据始终保持缓存,避免缓存雪崩,需要采取一些合理的缓存策略。以下是一些常见的合理方案:

1. 定时刷新缓存: 使用定时任务或调度器,定期刷新热门数据的缓存。这可以确保缓存中的数据始终保持最新,避免数据过期。

2. 永不过期策略:对于热门数据,可以设置永不过期的缓存策略。但要注意,如果热门数据发生变化,需要手动更新缓存。

3. 热点数据预加载:在应用启动时,预先加载热门数据到缓存中,确保缓存中存在最常用的数据。

4. 基于访问频率的过期策略: 根据数据的访问频率动态调整过期时间。访问频率高的数据设置较长的过期时间,访问频率低的数据设置较短的过期时间。

5. 分布式锁控制:** 在缓存失效时,使用分布式锁来防止多个请求同时查询数据库,确保只有一个请求进行查询并更新缓存。

6. 降级策略: 如果缓存失效,可以暂时使用降级策略,例如返回默认值或静态数据,以避免直接访问数据库。

7. 多级缓存: 使用多级缓存架构,将热门数据存储在多个缓存层中,例如内存缓存和分布式缓存,以提高数据的访问速度和稳定性。

8. 请求合并: 对于同时涌入的大量请求,可以考虑将它们合并成一个请求,只查询一次数据库,然后将结果分发给多个请求。

9. 缓存预热: 在系统负载较低的时候,提前将热门数据加载到缓存中,以减少在高负载时的数据库压力。

10. 动态缓存策略: 根据系统的实际情况,动态调整缓存策略,例如根据时间段、节假日等因素来设置不同的缓存策略。

选择合适的方案取决于你的业务需求和系统特点。通常,结合多个方案可以更好地保护热门数据,避免缓存雪崩问题。

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

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

相关文章

vue:this和that的理解

当我们进入公司的时候会发现一个很常见的情况&#xff0c;就是你的前开发者会常用这么一个变量&#xff1a;that、self… 为什么会用到that、self呢&#xff0c;小编是这么理解的&#xff0c;this指向的是当前的对象&#xff0c;而that、self是临时的变量&#xff0c;为了临时存…

水果成篮(力扣)双指针滑动窗口 JAVA

你正在探访一家农场&#xff0c;农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示&#xff0c;其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多地收集水果。然而&#xff0c;农场的主人设定了一些严格的规矩&#xff0c;你必须按照要求采摘水果&…

三重奏的和谐:如何完美对齐公司、部门与个人目标

引言 在企业的运营和管理中&#xff0c;目标的设定与对齐是至关重要的。它不仅决定了公司的方向和愿景&#xff0c;还影响到每一个部门和团队成员的工作内容和效果。如何确保公司目标、部门目标和团队个人目标之间的完美对齐&#xff0c;是每一个管理者都需要面对的挑战。 目…

JDK中的Timer总结

目录 一、背景介绍二、思路&方案三、过程1.Timer关键类图2.Timer的基本用法3.结合面向对象的角度进行分析总结 四、总结五、升华 一、背景介绍 最近业务中使用了jdk中的Timer&#xff0c;通过对Timer源码的研究&#xff0c;结合对面向对象的认识&#xff0c;对Timer进行针…

从NLP到聊天机器人

一、说明 今天&#xff0c;当打电话给银行或其他公司时&#xff0c;听到电话另一端的机器人向你打招呼是很常见的&#xff1a;“你好&#xff0c;我是你的数字助理。请问你的问题。是的&#xff0c;机器人现在不仅可以说人类语言&#xff0c;还可以用人类语言与用户互动。这是由…

获取excel中的图片(包含wps中嵌入单元格图片)

项目中有excel导入功能,并且需要导入excel中的图片;模板如图: 已知office中插入的图片为浮动形式;如图: wps中可以插入浮动图片,也可以插入嵌入单元格图片;如图: 并且在wps嵌入单元格形式的图片可以看到使用的是公式;如图: 问题来了,如何获取图片 并且将图片与单元格进行对应 …

Apache DolphinScheduler 支持使用 OceanBase 作为元数据库啦!

DolphinScheduler是一个开源的分布式任务调度系统&#xff0c;拥有分布式架构、多任务类型、可视化操作、分布式调度和高可用等特性&#xff0c;适用于大规模分布式任务调度的场景。目前DolphinScheduler支持的元数据库有Mysql、PostgreSQL、H2&#xff0c;如果在业务中需要更好…

【GitHub】Pycharm本地项目打包上传到Github仓库的操作步骤

文章目录 1、Pycharm端的设置操作2、Github端的设置操作3、Pycharm上配置Github4、Git本地项目至GitHub仓库5、前往Github中查看确认6、常见报错 1、Pycharm端的设置操作 通过CtrlAltS快捷组合键的方式&#xff0c;打开设置&#xff0c;导航到版本控制一栏中的Git&#xff0c;…

LTPP在线开发平台【使用教程】

LTPP在线开发平台 点击访问 LTPP在线开发平台 LTPP&#xff08;Learning teaching practice platform&#xff09;在线开发平台是一个编程学习网站&#xff0c;该网站集文章学习、短视频、在线直播、代码训练、在线问答、在线聊天和在线商店于一体&#xff0c;专注于提升用户编…

QT处理日志文件

由于实际生产需要&#xff0c;软件系统的运行&#xff0c;会产生大量的日志文件&#xff0c;有时候一天就能产生超过百万条log记录&#xff0c;那么为了能够处理日志文件&#xff0c;查询并且找到我们想要的报错信息&#xff0c;因此不得不考虑怎么实现&#xff0c;打开大日志文…

i.MX6ULL开发板无法进入NFS挂载文件系统的解决办法

问题 使用NFS网络挂载文件系统后卡住无法进入系统。 解决办法 此处不详细讲述NFS安装流程 查看板卡挂载在/home/etc/rc.init下的自启动程序 进入到../../home/etc目录下&#xff0c;查看rc.init文件&#xff0c;首先从第一行排查&#xff0c;查看/home/etc/netcfg文件代码内容&…

回归预测 | MATLAB实现FA-SVM萤火虫算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现FA-SVM萤火虫算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现FA-SVM萤火虫算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…

阿里云故障洞察提效 50%,全栈可观测建设有哪些技术要点?

本文根据作者在「TakinTalks 稳定性社区 」公开分享整理而成 #一分钟精华速览# 全栈可观测是一种更全面、更综合和更深入的观测能力&#xff0c;能协助全面了解和监测系统的各个层面和组件&#xff0c;它不仅仅是一个技术上的概念&#xff0c;更多地是技术与业务的结合。在“…

Zoho Books的安全性和数据保护:财务信息安全的保障措施揭秘

在信息化时代&#xff0c;如何保障企业信息安全是十分重要的问题&#xff0c;尤其是财务信息。财务管理工具的安全性是否有保障是许多用户担心的问题。 Zoho Books财务管理工具为客户提供了一系列的数据保护和安全措施&#xff0c;以确保客户财务信息的安全。 1. 采用高度加密…

MySQL流程控制

流程控制 顺序结构&#xff1a; 程序从上往下依次执行分支结构&#xff1a; 程序按条件进行选择执行&#xff0c;从两条或多条路径中选择一条执行。循环结构&#xff1a; 程序满足一定条件下&#xff0c;重复执行一组语句 针对于MySQL的流程控制语句主要有3类。注意&#xff…

js ajax 国内快速 映像

ajax 快速 映像 https://www.bootcdn.cn/ axios入门和axios基本请求方式 https://blog.csdn.net/m0_68997646/article/details/127438174 使用 jsDelivr CDN: <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>因为我们国…

jps(JVM Process Status Tool):虚拟机进程状况工具

jps&#xff08;JVM Process Status Tool&#xff09;&#xff1a;虚拟机进程状况工具 列出正在运行的虚拟机进程&#xff0c;并显示虚拟机执行主类名称&#xff08;Main Class&#xff0c;main()函数所在的类&#xff09;以及这些进程的本地虚拟机唯一ID&#xff08;LVMID&am…

第5步---MySQL的DQL查询语句

第5步---MySQL的DQL查询语句 DQL 数据库查询语言 1.基本的查询语句 1.完整得查询得语句 简化版的查询语句 select * from 表名 where 条件; 2.创建用于测试的表 1.创建测试数据 -- DQL -- 创建测试表 DROP TABLE IF EXISTS product; CREATE TABLE IF NOT EXISTS product( pi…

分代收集 + 垃圾回收算法

分代假说 1. 弱分代假说&#xff08;Weak Generational Hypothesis&#xff09;&#xff1a;绝大多数对象都是朝生夕灭的 2. 强分代假说&#xff08;Strong Generational Hypothesis&#xff09;&#xff1a;熬过越多次垃圾收集过程的对象就越难以消亡 3. 跨代引用假说&…

Java开发面试题 | 2023

Java基础 接口和抽象类的区别&#xff1f;Java动态代理HashMap 底层实现及put元素的具体过程currenthashmap底层实现原理&#xff1f;map可以放null值吗&#xff0c;currenthashmap为什么不能放null值synchronze和reetrantlock区别&#xff1f;怎样停止一个运行中的线程&#…