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

一 Redis雪崩、穿透和击穿

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

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

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

示例:

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

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

@Service
public class MovieService {@Autowiredprivate MovieRepository movieRepository;@Autowiredprivate 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 {@Autowiredprivate 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;@Autowiredpublic 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 {@Autowiredprivate 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.mzph.cn/news/40316.shtml

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

相关文章

机器学习笔记:李宏毅 stable diffusion

1 基本框架 ①&#xff1a;文字变成向量 ②&#xff1a;喂入噪声文字encoder&#xff0c;产生中间产物 ③&#xff1a;decoder 还原图片 2 text encoder 这张图越往右下表示效果越好&#xff0c;可以看到text encoder尺寸越大&#xff0c;对后续生成图片的增益越多 3 评价图…

公园游玩必备!新零售模式如何吸引更多游客

随着科技的不断演进&#xff0c;新零售模式正以前所未有的速度改变着我们的购物方式和消费体验。其中&#xff0c;自动售货机作为新零售模式的重要组成部分&#xff0c;以其智能化、便捷性和多样性的特点&#xff0c;正逐渐成为城市中熠熠生辉的一道风景线。 从24小时不间断的运…

LeetCode Top100 Liked 题单(序号34~51)

​34. Find First and Last Position of Element in Sorted Array ​ 题意&#xff1a;找到非递减序列中目标的开头和结尾 我的思路 用二分法把每一个数字都找到&#xff0c;最后返回首尾两个数 代码 Runtime12 ms Beats 33.23% Memory14 MB Beats 5.16% class Solution {…

前端练手小项目--自定义时间(html+css+js)

自定义时间 写文章的因 关于要写这篇文章的原因 是记录在工作上遇到的困难需求&#xff0c;是希望能给大家提供一些解决问题的思路 接下来我描述这个需求的多样性&#xff0c;难点在哪。 勾选勾选框开始时间与结束时间默认显示昨天与今天。取消勾选框开始时间与结束时间清空。…

如何查看线程在哪个cpu核上

1、ps -eLF查看PSR值 2、 taskset -pc $pid&#xff08;进程/线程&#xff09; 参考链接&#xff1a;https://blog.csdn.net/test1280/article/details/87993669

Ubuntu修改设置系列--修改ssh端口号的方法(有示例)

原文网址&#xff1a;Ubuntu修改设置系列--修改ssh端口号的方法(有示例)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Ubuntu修改ssh端口号的方法(有示例)。 要达成的目标 ssh添加一个端口&#xff1a;3333&#xff0c;关闭原来的22端口。 1.修改端口 修改配置文件/et…

thingsboard编译安装踩坑记录

thingsboard编译安装踩坑记录 一、编译&#xff1a;二、运行 朋友的thingsboard没人维护&#xff0c;要装新的服务器&#xff0c;啥文档也没有&#xff0c;就让参考官网的文档&#xff0c;版本也比较老3.2.2的&#xff0c;拿过来试了试记录下踩坑的地方。 一、编译&#xff1a;…

get与post如何拼接url与数据的灵活处理,循环的重要性。

get与post拼接url地址不同&#xff1a; let postData {method: "post",data: {op: "/api/setting/maintenanceperiod?period"this.authorizationCode,loadingConfig: {},data: {period:this.authorizationCode}}}; if(this.editData.id){let postData …

Nginx运行Vue项目:基本运行

需求 在Nginx服务器中&#xff0c;运行Vue项目。 说明 Vue项目打包生成的生产文件&#xff0c;是无法直接在浏览器打开的。需要放到Nginx服务器中&#xff0c;才能够访问。 本文章只介绍最基本的情况&#xff1a;Nginx中运行一个Vue项目。 实际生产环境&#xff0c;一个Ng…

mysql 批量给数据表和字段添加注释

1、用命令行导出 mysql数据库中的所有表 首先查看 mysql 的配置文件 “/etc/my.cnf ”&#xff0c;配置中找到 datadir 目录&#xff0c; 将文件导出到 datadir 目录下 我的 datadir 目录是&#xff1a; /var/lib/mysql 连接mysql&#xff0c;执行导出命令 SELECT TABLE_NAM…

解密 AI 客服;在不同硬件设备上运行大型语言模型的可能性

&#x1f989; AI新闻 &#x1f680; 微软必应首席执行官称必应聊天优于OpenAI的GPT-4&#xff0c;但成本更高 摘要&#xff1a;微软必应的首席执行官米哈伊尔・帕拉欣表示&#xff0c;必应聊天表现优于OpenAI的GPT-4&#xff0c;但使用了更高成本的检索增强推理技术。必应聊…

中科亿海微ROM使用

标题 ROM&#xff08;Read-Only Memory&#xff0c;只读存储器&#xff09;是一种在FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;中常用的存储器类型。与RAM&#xff08;Random Access Memory&#xff0c;机存取存储器&#xff09;…

Nginx安全加固,版本隐藏及HTTP请求头修改方法

1 隐藏nginx版本号 1.1 引言 nginx作为目前较为流行的http server软件&#xff0c;其相关的安全漏洞也非常多&#xff0c;攻击者可以根据我们的nginx版本来了解到相关的漏洞从而针对性的进行攻击。 通过新版本的nginx都会修复一些老版本的已知漏洞&#xff0c;但有时候我们生…

二刷LeetCode--148. 排序链表(C++版本),必会题,思维题

思路&#xff0c;本题其实考察了两个点&#xff1a;合并链表、链表切分。首先从1开始&#xff0c;将链表切成一段一段&#xff0c;因为需要使用归并&#xff0c;所以下一次的切分长度应该是当前切分长度的二倍&#xff0c;每次切分&#xff0c;我们拿出两段&#xff0c;然后将第…

虚拟机与Java虚拟机介绍

1、虚拟机 所谓虚拟机&#xff08;Virtual Machine&#xff09;&#xff0c;就是一台虚拟的计算机。它是一款软件&#xff0c;用来执行一系列虚拟计算机指令。大体上&#xff0c;虚拟机可以分为系统虚拟机和程序虚拟机。大名鼎鼎的Visual Box&#xff0c;VMware就属于 系统虚…

提示丢失vcomp140.dll怎么办?如何快速修复vcomp140.dll丢失问题

最近我遇到了一个程序启动失败的问题&#xff0c;错误提示显示缺少了vcomp140.dll文件。经过一番研究和尝试&#xff0c;我终于成功修复了这个问题。在这里&#xff0c;我将分享一下我的修复方法。 目录 vcomp140.dll是什么&#xff1f; 如何快速修复呢&#xff1f; vcomp140…

sCrypt编程马拉松于8月13日在复旦大学成功举办

继6月在英国Exeter大学成功举办了为期一周的区块链编程马拉松后&#xff0c;美国sCrypt公司创始人兼CEO刘晓晖博士带领核心团队成员王一强、郑宏锋、周全&#xff0c;于8月13日在复旦大学再次成功举办了一场全新的sCrypt编程马拉松。 本次活动由上海可一澈科技有限公司与复旦大…

C++笔记之花括号和圆括号初始化区别,列表初始化和初始化列表区别

C笔记之花括号和圆括号初始化区别&#xff0c;列表初始化和初始化列表区别 code review! 文章目录 C笔记之花括号和圆括号初始化区别&#xff0c;列表初始化和初始化列表区别1.花括号{}进行初始化和圆括号()进行初始化2.列表初始化&#xff08;list initialization&#xff0…

Vitis高层次综合学习——FPGA

高层次综合 什么是高层次综合&#xff1f;就是使用高级语言&#xff08;如C/C&#xff09;来编写FPGA算法程序。 在高层次综合上并不需要制定微架构决策&#xff0c;如创建状态机、数据路径、寄存器流水线等。这些细节可以留给 HLS 工具&#xff0c;通过提供输入约束&#xff…

专访阿里云席明贤,视频云如何运用大模型与小模型来破茧升级2.0

不久前&#xff0c;LiveVideoStack与阿里云视频云负责人席明贤&#xff08;花名右贤&#xff09;展开一场深度的对话&#xff0c;一个是圈内专业的社区媒体&#xff0c;一个是20年的IT老兵&#xff0c;双方有交集、有碰撞、有火花。 面对风云变幻的内外环境&#xff0c;阿里云…