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

【Java面试题】缓存穿透

什么是缓存穿透

缓存穿透是指当秒杀请求在Redis中未命中缓存时,系统会转而查询数据库。若数据库中也不存在该数据,大量此类请求将直接冲击数据库,造成数据库负载激增。
在这里插入图片描述

解决方案

缓存空值

当我们查询数据库发现数据库当中也不存在该数据时,这时候我们可以将空值缓存到Redis当中,这样下一次请求再次查询该数据时,就会从缓存中获取信息。
在这里插入图片描述

public String selectUser(String userId) {String cacheData = cache.get(userId);if (StrUtil.isBlank(cacheData)) {// 判断 Key 是否包含空值缓存,存在直接返回,不存在继续流程Boolean cacheIsNull = cache.hasKey("is-null_" + userId);if (cacheIsNull) {throw new RuntimeException();}String dbData = userMapper.selectId(userId);if (StrUtil.isNotBlank(dbData)) {cahce.set(userId, dbData);cacheData = dbData;} else {// 查询数据库中不存在数据,添加空值缓存并返回cache.set("is-null_" + userId, 较短过期时间);throw new RuntimeException();}}return cacheData;
}

但是这种方式也会存在一些弊端:当短时间内存在大量恶意请求时,缓存系统就会存在大量内存占用。

布隆过滤器

什么是布隆过滤器,布隆过滤器的原理

布隆过滤器是一种数据结构,用于快速判断一个元素是否存在于一个集合中。它以牺牲一定的准确性为代价,换取了存储空间的极大节省和查询速度的显著提升。

布隆过滤器是由一个位数组和一组哈希函数组成,当将数据存入数据库时,会先通过一组哈希函数,计算出该数据对应的哈希数值,再通过取模,将对应位数组的相应位置改为1。从而将数据进行标记。当查询数据库时,会先通过布隆过滤器检查该数据是否存在,如果该数据对应的位数组的位置全为1,则可能存在,继续查询数据库,反之,如果有任何一位为0,则一定不存在,不会去查询数据库。
在这里插入图片描述

布隆过滤器的优点和缺点

布隆过滤器的优点在于它可以高效地判断一个元素是否属于一个大规模集合,且具有极低的存储空间要求。如果存储 1亿元素,误判率设置为 0.001 也就是千分之一,仅需要占用 171M 左右的内存。

缺点在于可能会存在一定的误判率。

它在实际应用中常用于缓存场景下缓存穿透问题,对访问请求做一个快速判断机制。使用布隆过滤器能够有效减轻对底层存储系统的访问以及缓存系统的存储压力。

但是布隆过滤器本身也存在一些“弊端”,那就是不支持删除元素。因为它是一种基于哈希的数据结构,删除元素会涉及到多个哈希函数之间的冲突问题,这样会导致删除一个元素可能会影响到其他元素的正确性。

总的来说,布隆过滤器是一种非常高效的数据结构,适用于那些可以容忍一定的误判率的场合。

在这里插入图片描述
用布隆过滤器解决缓存穿透伪代码

public String selectUser(String userId) {String cacheData = cache.get(userId);if (StrUtil.isBlank(cacheData)) {if (!bloomFilter.contains(fullShortUrl)) {throw new RuntimeException();}String dbData = userMapper.selectId(userId);if (StrUtil.isNotBlank(dbData)) {cahce.set(userId, dbData);cacheData = dbData;}}return cacheData;
}

但是使用布隆过滤器还有可能发生哈希碰撞,导致判断错误。还是有可能导致该请求进入到数据库。那么接下来就需要将这些方法组合到一起使用。

布隆过滤器,缓存空值,分布式锁

当一个秒杀请求进入Redis,先判断Redis中是否有值,发现没有,接着去判断布隆过滤器中是否含有该数据,如果没有,直接返回无。如果监测到存在,就去先获取分布式锁,再去数据库当中进行查询。同时在查询之前还需要进行双重判断,即再次判断一下缓存中是否有值。(目的是防止该线程在等待锁期间其他线程已经查询到信息并将信息缓存到Redis当中)当从数据库中查询到后,再将该数据存入到Redis当中,并返回结果。
在这里插入图片描述
伪代码

public String selectUser(String userId) {String cacheData = cache.get(userId);if (StrUtil.isBlank(cacheData)) {// 判断 Key 是否存在布隆过滤器,存在则继续流程,否则直接返回if (!bloomFilter.contains(fullShortUrl)) {throw new RuntimeException();}// 判断 Key 是否包含空值缓存,存在直接返回,不存在继续流程Boolean cacheIsNull = cache.hasKey("is-null_" + userId);if (cacheIsNull) {throw new RuntimeException();}// 获取分布式锁Lock lock = getLock(userId);lock.lock();try {// 拿到锁之后进行双重判定,如果缓存已经存在则直接返回即可cacheData = cache.get(userId);if (StrUtil.isNotBlank(cacheData)) {return cacheData;}// 拿到锁之后进行双重判定,如果空值缓存已经存在则直接终止流程即可cacheIsNull = cache.hasKey("is-null_" + userId);if (!cacheIsNull) {throw new RuntimeException();}// 根据用户标识查询数据库记录String dbData = userMapper.selectId(userId);if (StrUtil.isNotBlank(dbData)) {cahce.set(userId, dbData);cacheData = dbData;} else {// 查询数据库中不存在数据,添加空值缓存并返回cache.set("is-null_" + userId, 较短过期时间);throw new RuntimeException();}} finally {lock.unlock();}}return cacheData;
}
http://www.lryc.cn/news/606954.html

相关文章:

  • 梯度下降的基本原理
  • Oracle EBS ERP开发 — 抛出异常EXCEPTION书写规范
  • Vue3 setup、ref和reactive函数
  • ReAct模式深度解析:构建具备推理能力的AI智能体架构
  • 【Linux】System V - 责任链模式与消息队列
  • 机密计算与AI融合:安全与智能的共生架构
  • 动态爱心树
  • Linux(CentOS 7.9) 卸载、安装MySql 5.7详细步骤教程,包括密码设置、字符集设置等
  • 鸿蒙系统PC安装指南
  • 无人机避让路径规划模块运行方式
  • 图论-最短路Floyd算法
  • SpringBoot与Rust实战指南
  • VS Code中配置使用slint(Rust)的一个小例子
  • Java学习第九十六部分——Eureka
  • 基于CNN卷积神经网络图像识别28个识别合集-视频介绍下自取
  • k8s之DevicePlugin
  • 运维端口管理闭环:从暴露面测绘到自动化封禁!
  • 自动驾驶的未来:多模态传感器钻机
  • 【通用视觉框架】基于OpenCvSharp+WPF+YOLO开发的仿VisionMaster的通用视觉框架软件,全套源码,开箱即用
  • CTF实战:用Sqlmap破解表单输入型SQL注入题(输入账号密码/usernamepassword)
  • 音频获取长度
  • armbian 启用nginx并设置访问密码
  • gpu instancer crowd 插件大规模渲染
  • 《操作系统真象还原》 第五章 保护模式进阶
  • 深度SEO优化的方式有哪些,从技术层面来说
  • WaitForSingleObject 函数参数影响及信号处理分析
  • 第15讲——微分方程
  • Shader开发(六)什么是着色器
  • 遥控器信号捕获
  • 软件反调试(7)- 基于NtSetInformationThread设置线程信息