Redis缓存穿透、击穿、雪崩面试题详解
缓存穿透
问题:
指的是客户端请求的数据在缓存中找不到,数据库中也没有存储,客户端还不断的发起请求。这样每次都无法在数据库查询到,缓存中永远没有这个数据。
这样的话,客户端一直去访问,会给后端数据库带来很大压力。
解决方案:
方案一:将空值存储到redis中
在查询数据库后,将该用户存储到redis中,值存储一个null值,这样下一次这个用户再来访问直接从redis中返回即可。但是为了不让这些没意义的数据一直存在占用内存,将有效时间设置短点。
- 优点:实现简单,维护方便
- 缺点:
- 会占用更多的内存消耗
- 造成短期的不一致
if ("数据" == null) { // 如果数据库中不存在 返回错误信息 存储到redis中 value设置为null,有效时间设置短点 防止出现缓存穿透两分钟 String key = "用户id"; stringRedisTemplate.opsForValue().set(key,"",2L,TimeUnit.MINUTES); }// 判断缓存是否为"" 直接返回 if ("".equals("数据")) {return "数据错误"; }
方案二:使用布隆过滤器
客户端每次发送请求,先去布隆过滤器中查询是否有没有这个数据,如果有直接返回,没有则去redis中查找。依次类推。
这个布隆过滤器复制了mysql中类似于字节的数据,所以可能出现过滤误判的情况,导致缓存穿透
- 优点:内存占用少,没有多余的key
- 缺点:
- 实现起来比较复杂
- 存在误判操作
其他方案:
- 增强id的复杂度,避免被猜测id规律
- 做好数据的基础格式校验
- 加强用户权限校验
缓存雪崩
问题:
指的是大量缓存集中在一个时间段失效或者redis服务宕机,从而大量请求去访问数据库,带来巨大压力。
缓存集体失效:
缓存服务器宕机:
这种情况比较严重
解决方案:
在每个缓存的失效时间上增加一个随机值,这样缓存的失效时间的重复率就会降低,很难再次引起缓存集体失效的事件。【不能解决缓存服务器宕机】
其他方案:
- 搭建redis集群,提高服务的可用性
- 一台宕机后,其他机器继续提供服务
- 给缓存业务添加降级限流策略
- 限制请求的并发数量
- 给业务添加多级缓存
- 添加多个缓存,减少访问数据库的频率
缓存击穿
问题:
对于缓存击穿也可以理解为热点key问题,就是一个被高并发访问而且缓存重建业务比较复杂的key突然失效(缓存中没有 数据库中有的数据),这时会有无数的请求访问数据库,造成数据库巨大压力。
解决方案:
方案一:互斥锁【加锁机制】
多个线程并发访问时,先拿到的锁先去查询数据库,别的线程需要等待【定时发起重试】,为了保证最后因为某种原因释放锁失败,所以在重建抢夺锁的时候,给锁设置一个有效期,做兜底方案。
- 优点:
- 没有额外的内存消耗
- 保证了数据的一致性
- 实现起来简单
- 缺点:
- 没有抢到锁的线程需要等待,性能受影响
- 可能发生死锁
锁逻辑:
// 获取锁 private boolean tryLock(String key){Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.SECONDS); return BooleanUtil.isTrue(isLock); } // 释放锁 private void unLock(String key){stringRedisTemplate.delete(key); }业务逻辑:
// 尝试获取到互斥锁 String lockKey = "lock:"+id; boolean tryLock = tryLock(lockKey); // 判断是否获取到锁 try { if (!tryLock) {// 没有拿到锁 Thread.sleep(50); return queryShopWithBreakdown(id); } // 拿到锁 // 再次判断缓存中是否有数据,防止别的线程中途重建 String key = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isNotBlank(key)){// 如果有数据直接返回 不需要重建 return JSONUtil.toBean(key,User.class); } // 不存在查询数据库 user = getById(id); // 防止后面线程抢先 Thread.sleep(500); if (user == null) {return null; } // 将返回结果存入redis中 设置有效期30分钟stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(user),30L,TimeUnit.MINUTES); } catch (InterruptedException e) { throw new RuntimeException(e); }finally { // 释放锁,中途发生异常也需要释放 unLock(lockKey); } // 返回数据 return user;
方案二:逻辑失效【热点数据】
使用逻辑失效时间,并不是TTL,是当前时间和过期时间相加得来。如果当前时间大于过期时间,就证明数据已过期,需要去更新缓存。同样需要抢夺互斥锁,但是抢夺不到的直接返回之前的数据,不会等待。抢夺到的开启一个新的线程负责更新缓存并重置过期时间,最后释放锁。
- 优点:线程无需等待,性能较好
- 缺点:
- 数据短时间不能保证一致
- 有额外的内存消耗
- 实现起来比较复杂
重建方法:
public void saveRedis(Long id,Long expire){ // 从数据库查询 User user = getById(id); RedisData redisData = new RedisData(); redisData.setData(user); // 当前时间和过期时间相加redisData.setExpireTime(LocalDateTime.now().plusSeconds(expire)); // 添加到缓存stringRedisTemplate.opsForValue().set(key+id,JSONUtil.toJsonStr(redisData));
}
业务逻辑:
// 判断缓存是否过期
RedisData redisdata = JSONUtil.toBean(userJson, RedisData.class);
JSONObject data = (JSONObject)redisdata.getData();
user = JSONUtil.toBean(data, User.class);
LocalDateTime expireTime = redisdata.getExpireTime();
// 过期时间是否在当前时间后面
if (expireTime.isAfter(LocalDateTime.now())){ // 没过期 return shop;
}
// 过期
// 尝试获取锁
String lockKey = "lock:"+id;
boolean isLock = tryLock(lockKey);
if (!isLock){ // 没有拿到锁 直接将之前过期数据返回 return shop;
}
try { // 拿到锁 开启一个线程 new Thread(new Runnable() {@Override public void run() { // 重建缓存 saveShop2Redis(id,20L); } }).start();
} catch (Exception e) {throw new RuntimeException(e);
}finally { unLock(lockKey);
}
// 返回数据
return shop;
相关文章:
Redis缓存穿透、击穿、雪崩面试题详解
缓存穿透 问题: 指的是客户端请求的数据在缓存中找不到,数据库中也没有存储,客户端还不断的发起请求。这样每次都无法在数据库查询到,缓存中永远没有这个数据。 这样的话,客户端一直去访问,会给后端数据…...
【网络安全】本地提权漏洞分析
0. 前言 CVE-2023-21752 是 2023 年开年微软第一个有 exploit 的漏洞,原本以为有利用代码会很好分析,但是结果花费了很长时间,难点主要了两个:漏洞点定位和漏洞利用代码分析,欢迎指正。 1. 漏洞简介 根据官方信息&a…...
电脑端(PC)按键精灵——3.其他命令
电脑端(PC)按键精灵——3.其他命令 前两节说了安装、键盘和鼠标命令,这一章说下其他命令 按键精灵小白入门详细教程: 电脑端(PC)按键精灵—小白入门 详细教程 命令介绍 1. Delay 延时 简介 //1秒=1000毫秒, 1分钟=60000毫秒,…...
Hudi集成Flink-写入方式
文章目录 一、CDC 入湖1.1、[开启binlog](https://blog.csdn.net/wuxintdrh/article/details/130142601)1.2、创建测试表1.2.1、创建mysql表1.2.2、将 binlog 日志 写入 kafka1、使用 mysql-cdc 监听 binlog2、kafka 作为 sink表3、写入sink 表 1.2.3、将 kakfa 数据写入hudi1、…...
深度探索list
1.list的基本组成 list是一个双向链表,它的基本组成就是 成员作用prev指针指向上一个元素next指针指向下一个元素data用来保存数据 2.list的迭代器 由于人们一般习惯于:迭代器是找到下一个元素,迭代器–是找到上一个元素。在双向链表list中…...
QQuick-自绘
QQuick提供了丰富的控件,搭配qml很容易就可以搭配出一套丝滑的UI界面。但是在有些场景下无论是出于效率还是现有控件的局限都需要进行自绘才能实现自身的需求。QQuick支持多种自绘: 可以使用的方案: 1. 继承QQuickPaintedItem ,重写 paint …...
【算法】【算法杂谈】已知[1,m]的等概率函数,求[1,n]的等概率函数
目录 前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本 思考感悟写在最后 前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介…...
【Python】Python中的列表,元组,字典
文章目录 列表创建列表获取元素修改元素添加元素查找元素删除元素列表拼接遍历列表切片操作 元组创建元组元组中的操作 字典创建字典添加/修改元素删除元素查找字典的遍历合法的key类型 列表 列表是一种批量保存数据的方式,列表使用[]表示 创建列表 创建两个空列…...
分布式系统概念和设计-分布式对象和远程调用
分布式系统概念和设计 分布式对象和远程调用 能够接收远程方法调用的对象称为远程对象,远程对象实现一个远程接口。 调用者和被调用对象分别存在不同的失败可能性,RMI和本地调用有不同的语义。 中间件 在进程和消息传递等基本构造模块之上提供编程模型的…...
11-FastDFS
一 为什么要使用分布式文件系统 单机时代 初创时期由于时间紧迫,在各种资源有限的情况下,通常就直接在项目目录下建立静态文件夹,用于用户存放项目中的文件资源。如果按不同类型再细分,可以在项目目录下再建立不同的子目录来区分…...
Word这样用,提高效率不加班
Word这样用,提高效率不加班 今天给大家分享23条Word文档的应用小技巧。对于大家来说,掌握些技巧能够效率百倍,何乐不为? 这些技巧是本人通过整理一直在用并且使用频率较高的,也希望能帮到大家。有兴趣的小伙伴可以自己…...
【Linux】调试器---gdb的使用
文章目录 一.背景知识二.安装gdb三.gdb的用法使用须知gdb的常用指令1.进入调试2.退出调试操作3.显示源代码4.设置断点breakPoint5.查看断点信息/禁用断点/开启断点/删除断点6.运行程序,开始调试run7.查看变量8.其它重要命令 一.背景知识 程序的发布方式有两种&…...
MySQL数据库之表的增删改查(进阶)
目录 1. 数据库约束1.1 约束类型1.2 NULL约束1.3 UNIQUE:唯一约束1.4 DEFAULT:默认值约束1.5 PRIMARY KEY:主键约束1.6 FOREIGN KEY:外键约束1.7 CHECK约束 2 表之间的关系2.1 一对一2.2 一对多2.3 多对多 3 新增4 查询4.1 聚合查…...
Nginx从开始到结束,简单到小白都能懂哦
绪论 大家好,很高兴能够为大家带来这篇关于Nginx配置的新手指南。在这篇博客中,我们将通过简单明了的图文教程,帮助大家快速上手Nginx配置,解锁Nginx的各种神奇功能! 一、Nginx简介 Nginx是一款功能强大的web服务器…...
Qt——Qt控件之按钮-QDialogButtonBox对话框按钮盒子控件的使用总结(例程:自定义按钮)
【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》...
数据库学习-常用的SQL语句
背景: 汇整一下自己学习数据库过程中常见的题目及语句。 一.实例分析题 二.简单SQL查询: 1):统计每个部门员工的数目select dept,count(*) from employee group by dept;2):统计每个部门员工的数目大于一个的记录se…...
5种获取JavaScript时间戳函数的方法
5种获取JavaScript时间戳函数的方法 一、JavasCRIPT时间转时间戳方法一:Date.now()方法二:Date.parse()方法三:valueOf()方法四:getTime()方法五:Number 二、js时间戳转时间方法一:生成2022/1/18 上午10:09…...
图的宽度优先遍历
文章目录 图的宽度优先遍历程序设计程序分析图的宽度优先遍历 【问题描述】根据输入图的邻接矩阵A,给出图的宽度优先遍历序列; 【输入形式】第一行为图的结点个数n,第二行输入顶点的信息,每个顶点用一个字符表示,接下来的n行为图的邻接矩阵A。其中A[i][j]=1表示两个结点邻…...
企业AD域(域控服务器)的安装和配置详细教程
一、环境以及工具准备 软件:VMWare Workstation 2016 ( 下载链接:https://pan.baidu.com/s/1iX1VRilerYPGbGvX4pvaKw 提取码:75R6 ) 镜像:Windows Server 2016 ( 下载地址ÿ…...
面试官:一千万的数据,你是怎么查询的?
面试官:一千万的数据,你是怎么查询的? 1 先给结论 对于1千万的数据查询,主要关注分页查询过程中的性能 针对偏移量大导致查询速度慢: 先对查询的字段创建唯一索引 根据业务需求,先定位查询范围(…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...







