Redis击穿,穿透和雪崩详解以及解决方案
在 Java 开发中,Redis 作为常用的缓存中间件,可能会面临击穿、穿透、雪崩这三类经典问题。以下是对这三个问题的详细解析及对应的 Java 解决方案:
一、Redis 缓存击穿(Cache Breakdown)
问题描述
- 定义:大量请求同时访问一个过期的热点 key(如秒杀活动中的商品库存),导致请求直接穿透到数据库,引发瞬时高并发压力。
- 核心原因:
- 热点 key 过期时,缓存失效。
- 大量并发请求同时绕过缓存,直达数据库。
Java 解决方案
1. 互斥锁(Mutex Lock)
- 思路:在缓存失效时,通过锁机制确保只有一个线程重建缓存,其他线程等待锁释放后从缓存获取数据。
- 实现步骤:
- 从 Redis 查询数据,若 key 过期或不存在,尝试获取分布式锁(如 Redisson、ZooKeeper 锁)。
- 获得锁的线程查询数据库,更新缓存,并释放锁。
- 其他线程在锁等待期间,休眠或重试查询缓存。
- Java 代码示例(基于 Redisson):
public String getProductInfo(String productId) {String cacheKey = "product:" + productId;String result = redisTemplate.opsForValue().get(cacheKey);if (result == null) { // 缓存失效RLock lock = redissonClient.getLock("mutex_lock:" + productId);try {lock.lock(); // 加锁// 二次验证(避免缓存重建期间其他线程重复查询)result = redisTemplate.opsForValue().get(cacheKey);if (result == null) {// 查询数据库String dbResult = queryFromDatabase(productId);if (dbResult != null) {redisTemplate.opsForValue().set(cacheKey, dbResult, 30, TimeUnit.SECONDS); // 重建缓存}}} finally {lock.unlock(); // 释放锁}}return result; }
2. 热点 key 永不过期
- 思路:为热点 key 设置逻辑过期时间(如在 value 中存储过期时间戳),通过异步线程更新缓存,避免主动过期导致的击穿。
- 实现步骤:
- 缓存数据时,在 value 中添加
expireTime
字段。 - 每次访问时,检查
expireTime
,若过期则启动异步线程更新缓存,当前请求仍返回旧数据。
- 缓存数据时,在 value 中添加
- Java 代码示例:
public class CachedData {private String value;private long expireTime;// getter and setter }public String getHotProductInfo(String productId) {String cacheKey = "hot_product:" + productId;CachedData cachedData = redisTemplate.opsForValue().get(cacheKey);if (cachedData == null || System.currentTimeMillis() > cachedData.getExpireTime()) {// 启动异步线程更新缓存(避免阻塞当前请求)CompletableFuture.runAsync(() -> {RLock lock = redissonClient.getLock("hot_product_lock:" + productId);try {lock.lock();// 二次验证cachedData = redisTemplate.opsForValue().get(cacheKey);if (cachedData == null || System.currentTimeMillis() > cachedData.getExpireTime()) {String dbResult = queryFromDatabase(productId);cachedData = new CachedData();cachedData.setValue(dbResult);cachedData.setExpireTime(System.currentTimeMillis() + 30 * 1000); // 逻辑过期时间redisTemplate.opsForValue().set(cacheKey, cachedData, 60, TimeUnit.SECONDS); // 物理过期时间设为逻辑过期时间的 2 倍}} finally {lock.unlock();}});// 返回旧数据或默认值(若首次查询)return cachedData != null ? cachedData.getValue() : defaultResponse();}return cachedData.getValue(); }
二、Redis 缓存穿透(Cache Penetration)
问题描述
- 定义:大量请求访问不存在的 key(如恶意攻击、非法参数),导致请求直接穿透缓存,每次都查询数据库,造成数据库压力激增。
- 核心原因:
- 缓存层不存储无效 key,导致所有无效请求直达数据库。
- 攻击方利用不存在的 key 进行批量请求。
Java 解决方案
1. 布隆过滤器(Bloom Filter)
- 思路:在请求进入数据库前,使用布隆过滤器过滤掉不存在的 key,避免无效请求到达数据库。
- 实现步骤:
- 提前将数据库中存在的 key 加载到布隆过滤器中。
- 每次请求先通过布隆过滤器判断 key 是否存在,若不存在则直接返回无效响应。
- Java 代码示例(基于 Google Guava):
// 初始化布隆过滤器(建议使用 Redis 存储布隆过滤器数据,避免内存溢出) private static BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), 1000000, // 预计元素数量0.01 // 误判率 );// 服务启动时加载现有 key 到布隆过滤器(示例) @PostConstruct public void loadExistingKeys() {List<String> productIds = productDao.getAllProductIds(); // 从数据库获取所有存在的 productIdbloomFilter.putAll(productIds); }public String getProductInfo(String productId) {if (!bloomFilter.mightContain(productId)) { // key 不存在return "无效的 productId";}// 正常查询缓存和数据库String cacheKey = "product:" + productId;String result = redisTemplate.opsForValue().get(cacheKey);if (result == null) {String dbResult = queryFromDatabase(productId);if (dbResult != null) {redisTemplate.opsForValue().set(cacheKey, dbResult, 30, TimeUnit.SECONDS);} else {// 缓存空值(防止重复查询)redisTemplate.opsForValue().set(cacheKey, "", 5, TimeUnit.MINUTES);}return dbResult;}return result; }
2. 缓存空值
- 思路:当数据库查询结果为 null 时,将空值存入缓存(设置较短过期时间),避免后续相同请求穿透到数据库。
- Java 代码示例:
public String getProductInfo(String productId) {String cacheKey = "product:" + productId;String result = redisTemplate.opsForValue().get(cacheKey);if (result == null) { // 缓存未命中String dbResult = queryFromDatabase(productId);redisTemplate.opsForValue().set(cacheKey, dbResult != null ? dbResult : "", // 空值存为 ""dbResult != null ? 30 : 5, // 存在数据则设正常过期时间,空值设短过期时间(如 5 分钟)TimeUnit.SECONDS);return dbResult;}return result.isEmpty() ? null : result; // 空值返回 null }
三、Redis 缓存雪崩(Cache Avalanche)
问题描述
- 定义:大量缓存 key 同时过期或 Redis 服务宕机,导致大量请求直接涌入数据库,造成数据库负载过高甚至崩溃。
- 核心原因:
- 缓存层大面积失效(如同一批次 key 的过期时间集中设置)。
- Redis 实例故障(如主从切换、集群节点宕机)。
Java 解决方案
1. 过期时间随机化
- 思路:为缓存 key 设置随机过期时间(在固定时间基础上增加随机偏移量),避免大量 key 同时过期。
- Java 代码示例:
public void setProductCache(String productId, String data) {int baseExpireTime = 30 * 60; // 30 分钟int randomOffset = ThreadLocalRandom.current().nextInt(10 * 60); // 随机偏移 0~10 分钟int expireTime = baseExpireTime + randomOffset;redisTemplate.opsForValue().set("product:" + productId, data, expireTime, TimeUnit.SECONDS); }
2. 限流与降级
- 思路:
- 限流:通过令牌桶、信号量等机制限制单位时间内进入数据库的请求量(如使用 Hystrix、Resilience4j 或 Spring Cloud Sentinel)。
- 降级:当数据库压力过大时,直接返回默认值或提示信息,保护数据库。
- Java 代码示例(基于 Resilience4j):
// 引入 Resilience4j 依赖 // 添加限流注解 @CircuitBreaker(name = "databaseCircuitBreaker", fallbackMethod = "fallbackGetProductInfo") public String getProductInfo(String productId) {String cacheKey = "product:" + productId;String result = redisTemplate.opsForValue().get(cacheKey);if (result == null) {String dbResult = queryFromDatabase(productId); // 可能触发限流if (dbResult != null) {redisTemplate.opsForValue().set(cacheKey, dbResult, 30, TimeUnit.SECONDS);}return dbResult;}return result; }// 降级方法 public String fallbackGetProductInfo(String productId, Throwable throwable) {log.error("数据库查询失败,productId: {}, error: {}", productId, throwable.getMessage());return "服务繁忙,请稍后重试"; // 返回默认值或提示 }
3. Redis 高可用架构
- 思路:搭建 Redis 集群(如 Sentinel 或 Cluster 模式),避免单点故障导致缓存层整体不可用。
- 配置示例(Spring Boot + Redis Cluster):
spring.redis.cluster.nodes=redis://node1:7000,redis://node2:7001,redis://node3:7002 spring.redis.cluster.max-redirects=3
四、总结对比
问题类型 | 核心原因 | 典型解决方案 | Java 关键技术 / 工具 |
---|---|---|---|
击穿 | 单个热点 key 过期 | 互斥锁、热点 key 永不过期 | Redisson、异步线程 |
穿透 | 大量无效 key 请求 | 布隆过滤器、缓存空值 | Guava BloomFilter、Redis 空值缓存 |
雪崩 | 大量 key 同时过期或 Redis 宕机 | 过期时间随机化、限流降级、高可用架构 | Resilience4j、Redis Cluster |
五、最佳实践建议
- 预防为主:
- 对热点数据提前预热缓存,避免突发流量击穿。
- 接口层做参数校验,拦截非法 key(如空值、格式错误)。
- 监控与报警:
- 监控 Redis 内存使用率、缓存命中率、过期 key 数量。
- 监控数据库 QPS、TPS,设置阈值触发报警。
- 综合方案:
- 针对高并发场景,组合使用互斥锁 + 布隆过滤器 + 限流降级,形成多层防护。
相关文章:
Redis击穿,穿透和雪崩详解以及解决方案
在 Java 开发中,Redis 作为常用的缓存中间件,可能会面临击穿、穿透、雪崩这三类经典问题。以下是对这三个问题的详细解析及对应的 Java 解决方案: 一、Redis 缓存击穿(Cache Breakdown) 问题描述 定义:大…...

网络渗透基础:信息收集
1.信息收集 whois xx.com 域名注册信息 注册人、电话、email Whois.chinaz.com kali自带whois工具 域名备案信息 Beian.miit.gov.cn Tianyancha.com Icp.chinaz.com 爱站 Sou.xiaolanben.com 2.子域名收集 收集方式 枚举:基于字典搜索引擎:googleh…...

[SAP] 如何查询当前屏幕的Tcode?
事务代码Tcode是SAP中到达特定屏幕的快捷路径 如何查询以下屏幕的事务码Tcode? 要浏览当前所使用的屏幕的事务码,可以选择System | Status 这里的事务代码是[VA22],它是Change Quotation的事务代码...

ZigBee 协议:开启物联网低功耗通信新时代
在物联网蓬勃发展的时代,无线通信技术犹如连接万物的桥梁,而 ZigBee 协议以其独特的优势,在众多通信协议中脱颖而出,成为构建低功耗、可靠物联网网络的关键技术之一。 一、ZigBee 协议的起源与发展 ZigBee 这个名字充满了自然的灵…...

JavaScript 模块系统:CJS/AMD/UMD/ESM
文章目录 前言一、CommonJS (CJS) - Node.js 的同步模块系统1.1 设计背景1.2 浏览器兼容性问题1.3 Webpack 如何转换 CJS1.4 适用场景 二、AMD (Asynchronous Module Definition) - 浏览器异步加载方案2.1 设计背景2.2 为什么现代浏览器不原生支持 AMD2.3 Webpack/Rollup 如何处…...

STM32F407寄存器操作(ADC非连续扫描模式)
1.前言 书接上回,在看手册的时候我突然发现手册上还描述了另一种ADC扫描模式,即非连续扫描模式,想着连续扫描模式都已经探索过了,那就顺手把非非连续模式研究一下吧。 2.理论 我们先看看手册,这里我就以规则通道举例…...
生产系统中TongWeb故障应急处理办法
本文档主要说明在上线正式运行的系统中,若TongWeb或部署在TongWeb上的应用出现问题时,现场维护人员或在现场的TongWeb支持人员应当采取的处理步骤。 工作基本原则: 任何操作必须经过项目相关负责人同意后进行,禁止在未允许的情况…...

PHP学习笔记(十一)
类常量 可以把在类中始终保持不变的值定义为常量,类常量的默认可见性是public。 接口中也可以定义常量。 可以用一个变量来动态调用类,但该变量的值不能为关键字 需要注意的是类常量只为每个类分配一次,而不是为每个类的实例分配。 特殊的…...
PyTorch中 torch.utils.data.DataLoader 的详细解析和读取点云数据示例
一、DataLoader 是什么? torch.utils.data.DataLoader 是 PyTorch 中用于加载数据的核心接口,它支持: 批量读取(batch)数据打乱(shuffle)多线程并行加载(num_workers)自…...

直线模组在手术机器人中有哪些技术挑战?
手术机器人在现代医疗领域发挥着越来越重要的作用,直线模组作为其关键部件,对手术机器人的性能有着至关重要的影响。然而,在手术机器人中使用直线模组面临着诸多技术挑战,具体如下: 1、高精度要求:手术…...

RK3568DAYU开发板-平台驱动开发--UART
1、程序介绍 本程序是基于OpenHarmony标准系统编写的平台驱动案例:UART 系统版本:openharmony5.0.0 开发板:dayu200 编译环境:ubuntu22 部署路径: //sample/06_platform_uart 2、基础知识 2.1、UART简介 UART指异步收发传输器(Univer…...
ubuntu 安装 Redis 5.0.8 的完整步骤
以下是根据前面的沟通记录整理的完整安装过程和依赖项,确保在 Ubuntu 22 上成功安装 Redis 5.0.8。 安装 Redis 5.0.8 的完整步骤 1. 安装依赖 在编译和运行 Redis 之前,需要安装一些必要的工具和库: sudo apt update sudo apt install bu…...

制造企业搭建AI智能生产线怎么部署?
制造商需要精准协调生产和发货,确保订单及时交付。MES、ERP、CRM 系统与生产线集成,对生产管理流程、物料跟踪、品控、确定货期至关重要。如果某个系统发生延迟或者效率低下,会在造成整个生产环节停滞,影响最终交付,导…...

深度学习驱动的超高清图修复技术——综述
Deep Learning-Driven Ultra-High-Definition Image Restoration: A Survey Liyan Wang, Weixiang Zhou, Cong Wang, Kin-Man Lam, Zhixun Su, Jinshan Pan Abstract Ultra-high-definition (UHD) image restoration aims to specifically solve the problem of quali…...
unix/linux source 命令,其内部结构机制
要理解 source (或 .) 命令的内部结构机制,我们需要戴上“操作系统”和“解释器设计”的眼镜,深入到 Shell 如何管理其状态以及如何执行命令的层面。 虽然我们无法直接看到 Shell 内部的 C 代码(除非我们去阅读 Bash 或 Zsh 的源码),但我们可以基于其行为和操作系统的原理…...
【LLM】FastAPI入门教程
note FastAPI 是一个现代的、快速(高性能)的 Web 框架,用于构建 API(应用程序编程接口)。它基于 Python 3.7,使用了 Python 类型提示(type hints),并且具有自动化的文档…...
进程同步机制-信号量机制-记录型信号量机制中的的wait和signal操作
wait和signal是记录型信号量机制中用于实现进程同步与互斥的两个重要操作, wait 操作 wait(semaphores *S) {S->value --;if (S->value<0) block(S->list) }请求资源:S->value --; 这一步表示进程请求一个单位的资源,将信号…...
gitlib 常见命令
git clone <项目URL> # 从 GitLab 拉取代码到本地 git status 查看状态 git diff 文件路径 查看修改位置 git diff 文件路径 查看修改位置 black -l 180 路径 格式化文件 git add 路径 (可以多个) 添加修改到暂存区 git commit -m “提交说明…...

Azure DevOps 管道部署系列之二IIS
本博客旨在提供如何使用 Azure DevOps YAML 管道部署到虚拟机上的 IIS 的实用指南。 开始之前,您需要做好以下准备: 您拥有要部署的服务器的访问权限以及 PowerShell 的管理员访问权限。您拥有要部署的远程服务器的互联网访问权限。您拥有在服务器上安装 .NET Core 托管包的…...

Vue.js教学第十七章:Vue 与后端交互(一),Axios 基础
Vue 与后端交互(一):Axios 基础 在现代前端开发中,Vue 应用通常需要与后端 API 进行数据交互,以实现动态数据的获取和提交。Axios 是一个基于 Promise 的 HTTP 客户端,广泛用于 Vue 项目中与后端进行通信。本文将深入讲解 Axios 的基本用法,包括如何通过 Axios 发送 GE…...

人工智能浪潮下,制造企业如何借力DeepSeek实现数字化转型?
一、DeepSeek技术概述 DeepSeek,凭借其强大的深度学习和自然语言处理能力,能够理解复杂问题并提供精准解决方案。它不仅能够作为学习、工作、生活的助手,满足用户在不同场景下的需求,更能在制造业中发挥重要作用。通过自然语言交…...
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
✅ 2.1 Express 的中间件机制?如何组织一个 RESTful API 项目? 面试官好,我来讲讲 Express 的中间件机制,它是 Express 架构的核心,也是组织 RESTful 项目的基础。 🧩 什么是中间件? 中间件&am…...

从线性代数到线性回归——机器学习视角
真正不懂数学就能理解机器学习其实是个神话。我认为,AI 在商业世界可以不懂数学甚至不懂编程也能应用,但对于技术人员来说,一些基础数学是必须的。本文收集了我认为理解学习本质所必需的数学基础,至少在概念层面要掌握。毕竟&…...

计算机网络相关发展以及常见性能指标
目录 一、因特网概述 1.1 基本概念 1.2 因特网发展的三个阶段 1.3 英特网服务提供者ISP 1.4 英特网的标准化工作 1.5 因特网的组成 1.6 简单总结 二、3种交换方式 2.1 电路交换(Circuit Switching) 2.2 分组交换(Packet Switching&…...

通义灵码:基于MCP的火车票小助手系统全流程设计与技术总结
具体操作步骤请访问:https://blog.csdn.net/ailuloo/article/details/148319336?spm1001.2014.3001.5502 前沿技术应用全景图 一、项目背景与需求分析 目标:基于12306 MCP接口,开发一款解决高峰出行(春运/节假日)痛…...

为什么建立 TCP 连接时,初始序列号不固定?
主要原因有两个方面: 很大程度上避免历史报文被下一个相同四元组的 TCP 连接接收问题(主要方面)防止黑客伪造相同序列号的 TCP 报文被接收 接下来,详细说说第一点 假设每次建立 TCP 连接时,客户端和服务端的初始序列…...

VBA数据库解决方案二十:Select表达式From区域Where条件Order by
《VBA数据库解决方案》教程(版权10090845)是我推出的第二套教程,目前已经是第二版修订了。这套教程定位于中级,是学完字典后的另一个专题讲解。数据库是数据处理的利器,教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...

NX753NX756美光科技闪存NX784NX785
技术解读与产品特性 美光科技的NX系列闪存,包括NX753、NX756、NX784、NX785等型号,代表了当前存储技术的前沿水平。这些产品基于先进的NAND闪存技术,采用业界领先的3D TLC NAND技术,实现了高速的数据读写能力。3D TLC NAND技术通…...

使用 pytesseract 构建一个简单 OCR demo
简介 pytesseract 库是 Google Tesseract OCR (光学字符识别)引擎的一个 Python 封装库,使用广泛且功能强大。 构建 使用 pytesseract 构建一个简单 OCR demo。 步骤一:安装必要的库 您需要在您的 Python 环境中安装 pytessera…...
Cesium快速入门到精通系列教程三:添加物体与3D建筑物
Cesium中添加物体与3D建筑物,对于大规模城市模型,推荐使用 3D Tileset;对于简单几何图形,可以使用 Entity API;对于复杂模型,可以使用 GLTF 格式: 一、添加一个点: 在 Cesium 1.93…...