【JAVA架构师成长之路】【Redis】第14集:Redis缓存穿透原理、规避、解决方案

30分钟自学教程:Redis缓存穿透原理与解决方案

目标

  1. 理解缓存穿透的成因及危害。
  2. 掌握布隆过滤器、空值缓存等核心防御技术。
  3. 能够通过代码实现请求拦截与缓存保护。
  4. 学会限流降级、异步加载等应急方案。

教程内容

0~2分钟:缓存穿透的定义与核心原因
  • 定义:恶意或异常请求频繁访问数据库中不存在的数据,绕过缓存直接冲击数据库。
  • 典型场景
    • 攻击者伪造大量非法ID(如负数、超长字符串)。
    • 业务未对查询参数校验,或未缓存空结果。
  • 危害
    • 数据库压力激增,甚至宕机。
    • 正常服务被恶意请求拖垮。

2~5分钟:代码模拟穿透场景(Java示例)
// 未做防护的查询方法(模拟穿透问题)  
public Product getProduct(String id) {  String key = "product:" + id;  Product product = redisTemplate.opsForValue().get(key);  if (product == null) {  // 直接查询数据库(未缓存空值)  product = productService.loadFromDB(id);  if (product != null) {  redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS);  }  }  return product; // 恶意请求会反复查询数据库  
}  

验证问题

  • 使用JMeter发送100次id=-1的请求,观察数据库查询次数是否为100次(穿透发生)。

5~12分钟:解决方案1——布隆过滤器(Bloom Filter)
  • 原理:基于位数组和哈希函数,快速判断数据是否可能存在于数据库,拦截非法请求。
  • 代码实现(Redisson布隆过滤器)
// 初始化布隆过滤器并预热合法数据  
public class BloomFilterInit {  private RBloomFilter<String> bloomFilter;  @PostConstruct  public void init() {  bloomFilter = redissonClient.getBloomFilter("product_bloom");  bloomFilter.tryInit(100000L, 0.01); // 容量10万,误判率1%  List<String> validIds = productService.getAllValidIds();  validIds.forEach(bloomFilter::add);  }  
}  // 查询时拦截非法请求  
public Product getProductWithBloomFilter(String id) {  if (!bloomFilter.contains(id)) {  return null; // 直接拦截  }  // 正常查询逻辑...  
}  
  • 注意事项
    • 误判率需根据业务容忍度调整(如0.1%更严格,但占用更多内存)。
    • 需定期同步布隆过滤器与数据库的合法数据(如定时任务)。

12~20分钟:解决方案2——空值缓存(Cache Null)
  • 原理:即使数据库不存在该数据,也缓存空值(如“NULL”),避免重复穿透。
  • 代码实现
public Product getProductWithNullCache(String id) {  String key = "product:" + id;  Product product = redisTemplate.opsForValue().get(key);  if (product == null) {  product = productService.loadFromDB(id);  if (product == null) {  // 缓存空值,5分钟过期  redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);  return null;  }  redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS);  } else if ("NULL".equals(product)) {  return null; // 直接返回空结果  }  return product;  
}  
  • 优化点
    • 空值过期时间不宜过长(避免存储大量无效Key)。
    • 可结合布隆过滤器,减少空值缓存的数量。

20~25分钟:解决方案3——请求参数校验
  • 原理:在业务层拦截非法参数(如非数字ID、越界值)。
  • 代码实现(Spring Boot参数校验)
public Product getProduct(@PathVariable String id) {  // 校验ID格式(仅允许数字)  if (!id.matches("\\d+")) {  throw new IllegalArgumentException("非法ID格式");  }  // 校验ID范围(如大于0)  long numericId = Long.parseLong(id);  if (numericId <= 0) {  throw new IllegalArgumentException("ID必须为正数");  }  // 正常查询逻辑...  
}  
  • 扩展
    • 使用Hibernate Validator实现注解式校验(如@Min(1))。

25~28分钟:应急处理方案
  1. 限流降级(Guava RateLimiter)
private RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个请求  public Product getProduct(String id) {  if (!rateLimiter.tryAcquire()) {  throw new RuntimeException("请求过于频繁,请稍后重试");  }  // 正常查询逻辑...  
}  
  1. 异步加载(CompletableFuture)
public Product getProductAsync(String id) {  String key = "product:" + id;  Product product = redisTemplate.opsForValue().get(key);  if (product == null) {  CompletableFuture.runAsync(() -> {  Product dbProduct = productService.loadFromDB(id);  if (dbProduct != null) {  redisTemplate.opsForValue().set(key, dbProduct, 1, TimeUnit.HOURS);  }  });  }  return product; // 可能返回空,但避免阻塞请求  
}  

28~30分钟:总结与优化方向
  • 核心原则:拦截非法请求、缓存空值、业务兜底。
  • 高级优化
    • 结合Redis Module的RedisBloom扩展(生产级布隆过滤器)。
    • 动态调整限流阈值(如根据数据库负载自动限流)。

练习与拓展

练习

  1. 实现一个布隆过滤器,拦截id<=0的非法请求。
  2. 修改空值缓存逻辑,动态设置随机过期时间(如5~15分钟)。

推荐拓展

  1. 学习RedisBloom模块的安装与使用。
  2. 研究分布式限流框架(如Sentinel)的实现原理。
  3. 探索缓存穿透与缓存击穿的综合防护方案。

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

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

相关文章

【Unity Shader编程】之光照模型

根据Unity Shader编程的光照模型实现&#xff0c;光线通常可分为以下核心组成部分&#xff1a; 一、基础光照分量 环境光&#xff08;Ambient&#xff09; 全局基础照明&#xff0c;不依赖具体光源 实现方式&#xff1a;UNITY_LIGHTMODEL_AMBIENT内置变量 漫反射光&#xff0…

dify + ollama + deepseek-r1+ stable-diffusion 构建绘画智能体

故事背景 stable-diffusion 集成进 dify 后&#xff0c;我们搭建一个小智能体&#xff0c;验证下文生图功能 业务流程 #mermaid-svg-6nSwwp69eMizP6bt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6nSwwp69eMiz…

无人机扩频技术对比!

一、技术原理与核心差异 FHSS&#xff08;跳频扩频&#xff09; 核心原理&#xff1a;通过伪随机序列控制载波频率在多个频点上快速跳变&#xff0c;收发双方需同步跳频序列。信号在某一时刻仅占用窄带频谱&#xff0c;但整体覆盖宽频带。 技术特点&#xff1a; 抗干扰…

【从零开始学习计算机科学】数字逻辑(九)有限状态机

【从零开始学习计算机科学】数字逻辑(九)有限状态机 有限状态机状态机的表示方法有限状态机的Verilog描述有限状态机 有限状态机(简称状态机)相当于一个控制器,它将一项功能的完成分解为若干步,每一步对应于二进制的一个状态,通过预先设计的顺序在各状态之间进行转换,状…

知识周汇 | Python操作Excel全攻略系列(四):单元格相关篇

目录 系列文章 1 重点 2 如何获取当前工作表的最大行数和最大列数 3 修改单元格的值&#xff08;包括数值和格式&#xff09; 4 查找特定字符的位置 5 对同一列中上下行值相同的单元格进行行合并 6 查找特定值并标记颜色 后续 系列文章 知识周汇 | Python操作Excel全攻…

KubeKey 与 KubeSphere:快速构建 Kubernetes 集群

深度解析 KubeKey 与 KubeSphere&#xff1a;快速构建现代化 Kubernetes 集群 一、技术栈概述 KubeKey&#xff08;KK&#xff09;是由 KubeSphere 团队开发的轻量级 Kubernetes 集群部署工具&#xff0c;其优势在于&#xff1a; 基于 Ansible 的安装程序具有大量软件依赖性…

C++编写Redis客户端

目录 安装redis-plus-plus库 ​编辑 编译Credis客户端 redis的通用命令使用 get/set exists del keys expire /ttl type string类型核心操作 set和get set带有超时时间 set带有NX string带有XX mset mget getrange和setrange incr和decr list类型核心操作…

从零开始实现大语言模型(十三):预训练大语言模型GPTModel

1. 前言 使用梯度下降算法通过下一个token预测任务预训练大语言模型GPTModel&#xff0c;前向传播流程每次会输入一个batch的长度均为context_len的训练样本&#xff0c;执行 batch_size context_len \text{batch\_size}\times\text{context\_len} batch_sizecontext_len次下…

JavaScript(最后一个元素的索引就是数组的长度减 1)array.length - 1

在不同的编程语言中&#xff0c;表示数组中最后一个元素的方法略有不同&#xff0c;但基本思路都是利用数组的长度或索引来实现。 以下是一些常见编程语言中获取数组最后一个元素的方法&#xff1a; 1. JavaScript: 使用 array.length - 1 索引: 这是最常见和传统的方法。Java…

RV1126+FFMPEG多路码流监控项目

一.项目介绍&#xff1a; 本项目采用的是易百纳RV1126开发板和CMOS摄像头&#xff0c;使用的推流框架是FFMPEG开源项目。这个项目的工作流程如下(如上图)&#xff1a;通过采集摄像头的VI模块&#xff0c;再通过硬件编码VENC模块进行H264/H265的编码压缩&#xff0c;并把压缩后的…

Python组合数据类型(一)

目录 一、数据类型 1、基本数据类型 2、组合数据类型 二、介绍两个函数 1、 isinstance函数 2、len函数 三、Python指针 1、指针 2、is运算符和的区别 3、列表的指针 四、函数参数的传递 1、例子一 2、例子二 五、字符串详解 1、转义字符 2、字符串的切片 3、字…

Doris vs ClickHouse 企业级实时分析引擎怎么选?

Apache Doris 与 ClickHouse 同作为OLAP领域的佼佼者&#xff0c;在企业级实时分析引擎该如何选择呢。本文将详细介绍 Doris 的优势&#xff0c;并通过直观对比展示两者的关键差异&#xff0c;同时分享一个企业成功用 Doris 替换 ClickHouse 的实践案例&#xff0c;帮助您做出明…

【ThreeJS Basics 09】Debug

文章目录 简介从 dat.GUI 到 lil-gui例子安装 lil-gui 并实例化不同类型的调整改变位置针对非属性的调整复选框颜色 功能/按钮调整几何形状文件夹调整 GUI宽度标题关闭文件夹隐藏按键切换 结论 简介 每一个创意项目的一个基本方面是能够轻松调整。开发人员和参与项目的其他参与…

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知&#xff0c;init进程为android系统的第一个进程&#xff0c;也是native世界的开端&#xff0c;要想让整个android世界能够稳定的运行&#xff0c;文件系统的创建和初始化是必不可少的&#xff…

Chain of Draft: 借鉴人类草稿思维让大型语言模型更快地思考

这个研究探讨了大型语言模型&#xff08;LLMs&#xff09;在执行复杂推理任务时面临的计算资源消耗与响应延迟问题。研究特别聚焦于思维链&#xff08;Chain-of-Thought, CoT&#xff09;提示范式的效率局限性。CoT虽然有效&#xff0c;但在推理过程中需要生成冗长、详尽的逐步…

《A++ 敏捷开发》- 18 软件需求

需求并不是关于需求 (Requirements are not really about requirements) 大家去公共图书馆寄存物品&#xff0c;以前都是扫二维码开箱&#xff0c;有些图书馆升级了使用指纹识别。 “是否新方法比以前好&#xff1f;”我问年轻的开发人员。 “当然用指纹识别好。新技术&#x…

【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别

1. 什么是智能体 将大语言模型作为一个推理引擎。给定一个任务&#xff0c; 智能体自动生成完成任务所需步骤&#xff0c; 执行相应动作&#xff08;例如选择并调用工具&#xff09;&#xff0c; 直到任务完成。 2. 先定义工具&#xff1a;Tools 可以是一个函数或三方 API也…

Vue进阶之Vue3源码解析(一)

Vue3源码解析 目录结构编译compiler-corepackage.jsonsrc/index.ts 入口文件src/compile.ts生成ASTsrc/parse.ts 代码转换src/transform.ts几种策略模式src/transforms/transformElement.tssrc/transforms/transformText.tssrc/transforms/transformExpression.ts 代码生成src/…

servlet tomcat

在spring-mvc demo程序运行到DispatcherServlet的mvc处理 一文中&#xff0c;我们实践了浏览器输入一个请求&#xff0c;然后到SpringMvc的DispatcherServlet处理的整个流程. 设计上这些都是tomcat servlet的处理 那么究竟这是怎么到DispatcherServlet处理的&#xff0c;本文将…

【我的待办(MyTodolists)-免费无内购的 IOS 应用】

我的待办&#xff08;MyTodolists&#xff09; 我的待办&#xff1a;智能任务管理助手应用说明主要功能为什么选择"我的待办"&#xff1f;隐私保障使用截图 我的待办&#xff1a;智能任务管理助手 应用说明 "我的待办"是一款智能化的任务管理应用&#x…