还不懂缓存穿透?Redis缓存穿透深度剖析

🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟
🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵
🍉本篇简介:🍉本篇记录Redis缓存穿透深度剖析命令操作,如有出入还望指正。

当系统中引入redis缓存后,一个请求进来后,会先从redis缓存中查询,缓存有就直接返回,缓存中没有就去db中查询,db中如果有就会将其丢到缓存中,但是有些key对应更多数据在db中并不存在,或者缓存大批量失效了,每次针对此次key的请求从缓存中取不到,请求都会压到db,从而可能压垮db。因此本篇就针对Redis缓存使用中存在的问题进行梳理,针对问题按照代码模拟现实场景并给出解决方案。

概述

当系统中引入redis缓存后,一个请求进来后,会先从redis缓存中查询,缓存有就直接返回,缓存中没有就去db中查询,db中如果有就会将其丢到缓存中,但是有些key对应更多数据在db中并不存在,或者缓存大批量失效了,每次针对此次key的请求从缓存中取不到,请求都会压到db,从而可能压垮db。因此本篇就针对Redis缓存使用中存在的问题进行梳理,针对问题按照代码模拟现实场景并给出解决方案。

关注公众号【可为编程】回复【加群】进入微信交流群一起学习!!!

缓存穿透

缓存穿透定义

穿透,顾名思义穿透缓存肯定是到数据库了,肯定是查询数据库不存在的数据,因为如果数据库中存在,查询一遍就存入到缓存了,就不会再次和数据库进行IO操作了。正因为数据库没有数据,导致每次请求都要到数据库中,失去了缓存的意义。总结一下造成缓存穿透的条件有:
1、数据库中没有符合请求条件的数据
2、请求穿透缓存频繁请求数据库
3、每次查询的值都不在redis中

缓存穿透场景

Redis起到保护数据库的作用,提升查询效率,如果连数据库都不存在对应数据,同时也就不会写入到Redis中,频繁查询就会和数据库进行频繁IO操作。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用大量此类攻击可能压垮数据库。那么有人会想了,可不可以做个校验呢?如果缓存中不存在用户信息,那么就存一下该用户信息,保证不到数据库不就不会压垮数据库了嘛,确实是这样。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

缓存穿透场景模拟

下面我根据现实的场景模拟一下,首先先去查询缓存,如果缓存不存在就去检索数据库,存在即返回。
Controller

@GetMapping("list")public String list(@Param("id") Integer id) {String re = redisUtils.get("test");//2. 缓存中没有数据,查询数据库System.out.println("缓存命中...");if (StringUtils.isEmpty(re)) {//2. 缓存中没有数据,查询数据款System.out.println("缓存不命中...查询数据库");Test test = testService.queryTest(id);return JSON.toJSONString(test);}System.out.println(re);return re;}

service

 public Test queryTest(Integer id) {Test row = testMapper.queryTest(id);System.out.println("查询数据库");return row;}

我们请求参数id传一个数据库不存在该条数据的id,肯定直接打到数据库。
在这里插入图片描述
数据库中也没有该数据,如果处于高并发情况下这种场景直接造成数据库宕机,因此我们可以将查询出来的null结果存入到缓存,只需要第一次查询的时候检索数据库,后面直接命中缓存返回结果。修改service。

public Test queryTest(Integer id) {Test row = testMapper.queryTest(id);System.out.println("查询数据库");//查询数据库后将对象存入缓存redisUtils.set("test", JSON.toJSONString(row));redisUtils.expire("test", 30, TimeUnit.MINUTES);return row;}

在这里插入图片描述
第一次查询数据库并存入缓存,第二次直接查询缓存,看似没有问题,逻辑很合理,但是在高并发的场景下就会出问题了,我们采用Jmater进行压力测试,模拟100个并发请求同时请求查询接口。
在这里插入图片描述
因为多线程场景下存在线程抢占机制,都在查询缓存然后查询数据库,第一个线程来了,看到缓存没有,就去查询数据库,第二个线程来了发现缓存还没有,继续查询数据库,当在查询出来数据与存入缓存环节的空隙时间内,多个请求已经打到数据库了,所以我们要保证并发情况下的操作原子性。由于springboot所有的组件都是单例的,即使有批量请求也让他访问查询和存入缓存的操作是使用同一把锁,所以可以使用synchronized (this)来加锁,第一个请求来时获取锁,查询数据库,在查询之前再次确认下缓存中是否有数据,如果没有则从数据库中查询,获取到数据后存入缓存中。
修改service方法实现

public Test queryTest(Integer id) {//只要是同一把锁就可以锁住所有的线程//1. synchronized (this): springboot所有的组件都是单例的,即使有批量请求也是使用同一把锁synchronized (this) {//关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!//得到锁以后,应该先去缓存中确定一次,如果没有再进行查询String test = redisUtils.get("test");if (!StringUtils.isEmpty(test)) {//缓存不为空直接返回return (Test) JSON.toJSON(test);}//将数据库的多次查询变为一次Test row = testMapper.queryTest(id);System.out.println("查询数据库");//查询数据库后将对象存入缓存redisUtils.set("test", JSON.toJSONString(row));redisUtils.expire("test", 30, TimeUnit.MINUTES);return row;}}

我们再次执行之后就发现不会出现刚才那种场景了,只查询了一次数据库,其他请求都没有打到数据库上面。
在这里插入图片描述

缓存不命中...
缓存不命中...
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
==> Parameters: 5(Integer)
从缓存中获取数据时出现异常,key:test,value:null
java.lang.NullPointerException: null
<==      Total: 0
查询数据库
缓存命中...
null
缓存不命中...
缓存不命中...
缓存不命中...
缓存不命中...
缓存命中...
null
缓存命中...
null

多次执行的结果是不一样的,线程的优先级和时间片分配可能影响线程的执行顺序和时长,进而影响多线程程序的结果。在实际的生产环境中我们主要是采用消息中间件来接受并发请求,按顺序逐一进行处理,同时引入多线程提高任务执行效率。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

缓存穿透解决方案

可以在缓存中存一个空字符串,或者其他特殊字符串用于标识该条为空的数据,然后当应用拿到这个特殊字符串的时候表示数据库没有值,就没必要再去查询数据库了。但是存特殊字符的办法只适用于重复查询同一个不存在的值的情况,如果每次请求,ID都是可变的,并假设ID符合规则,但是每次变化的值都不存在于数据库中,那请求还是会打到数据库中。伪代码如下:

while(true){where id = random();
}

所以总结一下几个比较好的解决方案:
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
1、对空值缓存
如果一个查询返回的数据为空(不管数据库是否存在),我们仍然把这个结果(null)进行缓存,给其设置一个很短的过期时间,最长不超过五分钟。不然新增了这条数据后,查询还是查不到,保证在后续新增之后不会影响数据查询。
2、设置可访问的名单(白名单)
使用redis中的bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问的id不在bitmaps里面,则进行拦截,不允许访问

(3)采用布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的,它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

布隆过滤器可以用于检测一个元素是否在一个集合中,它的优点是空间效率和查询的时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。后面会单独对其进行介绍。

(4)进行实时监控
当发现redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制对其提供服务(比如:IP黑名单)

今天写太慢了,明天争取将缓存雪崩和缓存击穿一起写出来,重在学习,重在消化。
在这里插入图片描述

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

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

相关文章

【云备份】数据管理模块

文章目录 1. 数据管理模块要管理什么数据&#xff1f;2. 数据管理模块如何管理数据&#xff1f;3. 数据管理模块的具体实现BackupInfo 数据信息类NewBackupInfo —— 获取各项属性信息 DataManager 数据管理类构造函数析构函数insert —— 新增update —— 修改GetOneByURL——…

Portraiture2024最新Photoshop磨皮插件更新啦

Portraiture是一款由Imagenomic公司研发的Photoshop磨皮插件。该插件以其优秀的磨皮效果&#xff0c;成为了众多摄影师和化妆师使用的首选插件。Portraiture主要用于影楼、婚纱、时尚摄影等各个领域。其主要特点是能够轻松地模拟人眼的视觉感受&#xff0c;自然地修饰人像照片。…

netty(三) taskQueue自定义任务,http服务器快速入门,netty核心模块,Unpooled

如果执行某些业务比较复杂&#xff0c;比较耗时&#xff0c;可以使用异步来完成 当然可以有多个任务 上面的结果是&#xff0c;在第一个任务处理完&#xff0c;再等20秒执行&#xff0c;简单来说&#xff0c;就是第一个在10秒执行&#xff0c;第二个在第30秒的时候执行&#…

Linux:通过VMWare,定制化Linux系统

一、原理图 二、新增磁盘&#xff08;对应上图sdb盘&#xff09; 三、挂载磁盘 主要是四步&#xff1a;查看磁盘&#xff0c;分区磁盘&#xff0c;格式化磁盘&#xff0c;挂载磁盘 1、查看磁盘 2、分区磁盘 3、格式化磁盘 4、挂载磁盘 创建两个备用目录&#xff0c;用于磁盘…

Qt_一个由单例引发的崩溃

Qt_一个由单例引发的崩溃 文章目录 Qt_一个由单例引发的崩溃摘要关于 Q_GLOBAL_STATIC代码测试布局管理器源码分析Demo 验证关于布局管理器析构Qt 类声明周期探索更新代码获取父类分析Qt 单例宏源码 关键字&#xff1a; Qt、 Q_GLOBAL_STATIC、 单例、 UI、 崩溃 摘要 今…

网页设计--第5次课后作业

1、快速学习JavaScript的基本知识第11-14章 JavaScript入门 - 绿叶学习网 2、使用所学的知识完成以下练习。 1&#xff09;点击 “点亮”按钮 点亮灯泡&#xff0c;点击“熄灭”按钮 熄灭灯泡 2&#xff09;输入框鼠标聚焦后&#xff0c;展示小写&#xff1b;鼠标离焦后…

【Java】实现一个自己的线程池

上文中我们讲了线程池的简单使用&#xff0c;这里我们来讲一下如何简单实现一个自己的线程池 本文实现这个线程池所达到的效果是&#xff1a;用户给出线程数目&#xff0c;程序根据用户给出的数创建固定数目的线程 1、框架 首先写定义一个线程池类 class MyThreadPool{}pub…

振南技术干货集:znFAT 硬刚日本的 FATFS 历险记(4)

注解目录 1、znFAT 的起源 1.1 源于论坛 &#xff08;那是一个论坛文化兴盛的年代。网友 DIY SDMP3 播放器激起了我的兴趣。&#xff09; 1.2 硬盘 MP3 推了我一把 &#xff08;“坤哥”的硬盘 MP3 播放器&#xff0c;让我深陷 FAT 文件系统不能自拔。&#xff09; 1.3 我…

前端入门(三)Vue生命周期、组件原理、脚手架、插槽插件、存储、组件事件、动画、跨域与代理

文章目录 Vue生命周期Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构组件相关高级属性引用名 - ref数据接入 - props混…

起猛了!自动驾驶可以聊天了

公众号&#xff1a;算法一只狗 一直以来&#xff0c;深度学习模型对于研究者来说是一种“黑盒”模型&#xff0c;我们一般很难分析模型的到底学习到了什么东西&#xff0c;只知道它能够应用于特定的领域。 对比于自动驾驶领域来看&#xff0c;一个黑盒状态的模型&#xff0c;是…

听说你把 ChatGPT 当成搜索引擎用了?

未经作者&#xff08;微信ID&#xff1a;Byte-Flow&#xff09;允许&#xff0c;禁止转载 文章首发于公众号&#xff1a;字节流动 看到一些读者朋友把 ChatGPT 当成搜索引擎使用了&#xff0c;当然这样使用也没有问题&#xff0c;只是并不能发挥出 ChatGPT 背后大型语言模型&am…

​ 云计算的尽头是轻量应用服务器?带你了解亚马逊Lightsail的卓越优势

很多小伙伴可能都在好奇&#xff0c;轻量应用服务器有什么特点&#xff1f;为什么相较于普通的云服务器&#xff0c;很多用户现在都更青睐于轻量应用服务器?这个“轻”就是它独特的优势所在。轻量应用服务器以其“开箱即用、应用优质、轻松上手、投入划算、运维便捷和稳定可靠…