当前位置: 首页 > news >正文

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 ( 下载地址&#xff…...

面试官:一千万的数据,你是怎么查询的?

面试官:一千万的数据,你是怎么查询的? 1 先给结论 对于1千万的数据查询,主要关注分页查询过程中的性能 针对偏移量大导致查询速度慢: 先对查询的字段创建唯一索引 根据业务需求,先定位查询范围&#xff08…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

django filter 统计数量 按属性去重

在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...