Redis 击穿、穿透与雪崩:深度解析与应对策略
在使用 Redis 作为缓存的系统架构中,缓存击穿、穿透和雪崩是三个常见且可能对系统性能产生严重影响的问题。深入理解这些问题并掌握有效的应对策略对于构建稳定、高效的系统至关重要。
一、缓存击穿
(一)问题描述
缓存击穿是指一个热点 key 在缓存中突然过期,而此时大量并发请求同时访问这个 key,由于缓存中不存在该数据,这些请求会直接穿透到数据库查询数据,并且在查询到数据后重新将数据写入缓存。在高并发场景下,数据库可能瞬间承受巨大的压力,甚至导致数据库服务不可用。
例如,在一个电商系统中,某个热门商品的详情信息被缓存了。当缓存过期时,恰好有大量用户同时点击查看该商品详情,这些请求就会同时涌向数据库获取商品数据,可能使数据库负载急剧上升。
(二)处理方法
- 设置热点数据永不过期:对于一些极端热点且更新频率极低的数据,可以设置其在缓存中永不过期。这样可以避免因缓存过期导致的击穿问题,但需要注意数据一致性的维护,当数据有更新时,要及时更新缓存中的数据。
- 使用互斥锁:当缓存中不存在热点 key 时,在访问数据库之前先获取一个互斥锁。只有获取到锁的线程才能去数据库查询数据并更新缓存,其他线程则等待。在获取锁的线程完成数据库查询并更新缓存后,释放锁,其他线程再从缓存中获取数据。示例代码如下(以 Java 语言使用 Redis 的 Jedis 客户端为例):
java
public String getValue(String key) {String value = redis.get(key);if (value == null) {// 获取锁if (redis.setnx(key + "_lock", "locked") == 1) {try {// 再次检查缓存,防止其他线程已经更新缓存value = redis.get(key);if (value == null) {// 从数据库查询数据value = db.query(key);// 将数据写入缓存redis.set(key, value);}} finally {// 释放锁redis.del(key + "_lock");}} else {// 等待一段时间后重试Thread.sleep(100);return getValue(key);}}return value;
}
这种方式可以有效控制并发访问数据库的数量,但会增加系统的响应时间,因为线程需要等待锁的获取。
二、缓存穿透
(一)问题描述
缓存穿透是指查询一个根本不存在的数据,缓存和数据库都不会命中,这样的请求每次都会穿透缓存到达数据库,导致数据库压力增大。如果攻击者恶意构造大量这样的请求,可能会使数据库服务崩溃。
例如,在一个用户系统中,攻击者故意发送大量不存在的用户 ID 查询请求,这些请求都会直接访问数据库,数据库不断进行无效查询操作,浪费大量资源。
(二)处理方法
- 缓存空对象:当查询数据库未找到数据时,将空对象缓存起来,并设置一个较短的过期时间。这样后续相同的查询请求就可以直接从缓存中获取空结果,避免再次查询数据库。示例代码如下:
java
public String getValue(String key) {String value = redis.get(key);if (value == null) {// 从数据库查询数据value = db.query(key);if (value == null) {// 将空对象缓存起来,设置过期时间为 60 秒redis.setex(key, 60, "");} else {// 将数据写入缓存redis.set(key, value);}}return value;
}
但这种方法可能会导致缓存中存储大量无用的空对象数据,占用缓存空间,并且如果数据库中数据后续有更新,可能会出现数据不一致的情况。
2. 布隆过滤器:在访问缓存之前,先使用布隆过滤器判断请求的数据是否可能存在于数据库中。布隆过滤器是一种基于位图的数据结构,可以高效地判断一个元素是否在集合中,但存在一定的误判率(误判为存在但实际不存在)。如果布隆过滤器判断数据可能不存在,那么直接返回空结果,不再查询缓存和数据库;如果判断数据可能存在,则继续正常的缓存查询流程。示例代码如下(以 Guava 库中的布隆过滤器为例):
java
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;public class BloomFilterExample {private static final int EXPECTED_INSERTIONS = 1000000;private static final double FPP = 0.01;private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), EXPECTED_INSERTIONS, FPP);static {// 初始化布隆过滤器,将数据库中已有的数据对应的 key 加入布隆过滤器List<Integer> existingKeys = db.getAllKeys();for (Integer key : existingKeys) {bloomFilter.put(key);}}public String getValue(String key) {int keyInt = Integer.parseInt(key);if (!bloomFilter.mightContain(keyInt)) {return "";}String value = redis.get(key);if (value == null) {value = db.query(key);if (value == null) {redis.setex(key, 60, "");} else {redis.set(key, value);}}return value;}
}
使用布隆过滤器可以有效减少缓存穿透的发生,但需要合理设置误判率和预估数据量,并且在数据库数据有新增或删除时,需要及时更新布隆过滤器。
三、缓存雪崩
(一)问题描述
缓存雪崩是指在短时间内,大量缓存中的 key 同时过期或者 Redis 服务突然不可用,导致大量请求直接访问数据库,数据库压力瞬间剧增,可能导致数据库服务崩溃,进而影响整个系统的正常运行。
例如,在一个社交系统中,很多用户的动态数据都被缓存了,并且设置了相同的过期时间。当这些缓存同时过期时,大量用户的请求就会同时涌向数据库获取动态数据,使数据库不堪重负。
(二)处理方法
- 设置缓存过期时间随机化:将缓存的过期时间设置为一个随机值,避免大量缓存同时过期。例如,可以在原本设置的过期时间基础上,加上一个随机的时间偏移量,使不同 key 的过期时间分布在一个时间段内。示例代码如下:
java
public void setValue(String key, String value) {// 原本设置的过期时间为 600 秒int baseExpireSeconds = 600;// 随机生成 0 - 300 秒的偏移量int randomOffset = new Random().nextInt(300);// 最终的过期时间int expireSeconds = baseExpireSeconds + randomOffset;redis.setex(key, expireSeconds, value);
}
- 使用缓存预热:在系统启动或缓存服务重启时,提前将一些热点数据加载到缓存中,避免在用户请求高峰时因缓存未命中而导致大量请求穿透到数据库。可以在系统启动时编写一个数据加载脚本,从数据库中查询热点数据并写入缓存。
- 搭建高可用的 Redis 集群:使用 Redis 集群可以提高 Redis 的可用性和容错性。当部分节点出现故障时,集群中的其他节点可以继续提供服务,减少因 Redis 服务不可用导致的雪崩影响。常见的 Redis 集群方案有主从复制、哨兵模式和 Redis Cluster 等。例如,在主从复制模式下,主节点负责写操作,从节点负责读操作,从节点会定期从主节点同步数据。当主节点故障时,可以手动或自动将从节点提升为主节点,继续提供服务。
- 限流与降级:在系统中引入限流和降级机制,当发现缓存雪崩发生且数据库压力过大时,对部分非核心业务的请求进行限流,只允许一定数量的请求通过,或者直接对这些非核心业务进行降级处理,返回默认数据或提示信息,以保护数据库和整个系统的稳定性。例如,可以使用令牌桶算法或漏桶算法进行限流,在代码中根据业务的重要性设置不同的限流阈值和降级策略。
综上所述,缓存击穿、穿透和雪崩是 Redis 缓存使用过程中需要重点关注的问题。通过合理设置缓存过期时间、使用互斥锁、缓存空对象、布隆过滤器、缓存预热、搭建高可用集群以及限流降级等多种技术手段,可以有效地预防和应对这些问题,提高系统的性能、稳定性和可靠性,为用户提供更好的服务体验。在实际的系统开发和运维中,需要根据系统的业务特点、数据规模和并发量等因素,综合运用这些策略,不断优化系统架构和缓存策略。
相关文章:
Redis 击穿、穿透与雪崩:深度解析与应对策略
在使用 Redis 作为缓存的系统架构中,缓存击穿、穿透和雪崩是三个常见且可能对系统性能产生严重影响的问题。深入理解这些问题并掌握有效的应对策略对于构建稳定、高效的系统至关重要。 一、缓存击穿 (一)问题描述 缓存击穿是指一个热点 key…...

8086处理器的寻址方式
概念 在计算机系统中,处理器操作和处理的是数值,那么,必定涉及数值从哪里来,处理后送到哪里去,这称为寻址方式(Addressing Mode)。 简单地说,寻址方式就是如何找到要操作的数据,以及如何找到存…...

Mask实现裁剪的原理浅析
简单来说,就是Mask会设置继承了MaskableGraphic的组件的Shader属性,进行特定的模板测试 一张普通的Image,当不挂Mask组件时,其默认Shader的模板缓存属性是这样的 当挂载上Mask时,会改变 Stencil ID变成了1ÿ…...

每隔一秒单片机向电脑发送一个16进制递增数据
SCON0x50 SM00 SM11(工作方式为方式一) REN1允许单片机从电脑接收数据 TB8 RB8 SM2是方式2和方式3直接配置为0 TI为发送中断请求标志位 由硬件配置为1 必须由 软件复位为0,RI为接收中断请求标志位,同理TI UART.c #include &l…...

逆向攻防世界CTF系列56-easy_Maze
逆向攻防世界CTF系列56-easy_Maze 64位无壳,看题目就知道是迷宫问题了 int __fastcall main(int argc, const char **argv, const char **envp){__int64 v3; // raxint v5[7][7]; // [rsp0h] [rbp-270h] BYREFint v6[104]; // [rspD0h] [rbp-1A0h] BYREFv6[52] 1…...

【Linux网络编程】应用层:HTTP协议 | URL | 简单实现一个HTTP服务器 | 永久重定向与临时重定向
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站 🌈个人主页: 南桥几晴秋 🌈C专栏: 南桥谈C 🌈C语言专栏: C语言学习系…...

电压调整电路汇总
目录: 一、LDO 1、LM1117 2、NCV33275 3、TLE42764 4、TPS7B67xx-Q1 5、总结 二、DCDC转换器 1、LM2576与LM2596 2、MC34063 一、LDO 1、LM1117 LM1117 是一款在 800mA 负载电流下具有 1.2V 压降的低压降稳压器。 LM1117 提供可调节电压版本,…...

day28 文件IO及进程线程基础
讨论光标共享情况 1.dup和dup2定义变量赋值都共享光标 2.使用两个描述符调用两次open函数打开同一个文件,不共享光标 #include <myhead.h>int main(int argc, const char *argv[]) {//1、描述符赋值给新的变量char buff[1024] "abcdefg";int ne…...

【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍
本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 前言 随着无服务计算的兴起和大数据环境中的数据集成需求, 需要使用某些轻量级的服务,来实现一些简单操作。因此Azure Function就成了微软云上的一个必不可少的组成部分。 …...
前端文件下载
这里写自定义目录标题 前端文件下载方法使用a标签使用iframe标签二进制流 前端文件下载方法 使用a标签 /*** 文件下载方法 使用a标签* 存在浏览器下载时,太快的话,会取消上次的下载请求* param {*} href* param {*} filename*/ export function downlo…...

前端成长之路:HTML(3)
在HTML中,有列表标签。列表最大的特点是整齐、简洁、有序,用列表进行布局会更加自由方便。根据使用的情景不同,可以将列表分为三大类:无序列表、有序列表和自定义列表。 无序列表 在HTML中使用<ul>标签定义一个无序列表&a…...

无人机自动机库的功能与作用!
一、无人机自动机库的功能 智能停放与管理 无人机自动机库为无人机提供了一个安全、可靠的停放环境。通过先进的感知技术和安全防护措施,它能够实时监测周围环境,确保无人机免受恶劣天气或潜在风险的侵害。 无人机在机库内可以实现智能停放࿰…...

ubuntu 新建脚本shell并增加图标 双击应用实现python运行
1.使用nano创建shell脚本文件 需要在终端窗口中输入“nano”以打开文本编辑器。 nano 在创建脚本文件前,我们要了解脚本文件是如何运行的: 直接运行:直接在终端直接输入需要运行的脚本文件名称,系统或用缺省版本的shell运行脚…...
ANR 分析SOP
遇到ANR问题不要慌,大部分情况下可能是系统or测试手段问题,我们按照如下关键字排查定位 文章目录 1 是否是 heapdump 导致?1.1 dump开始1.2 dump结束 1 是否是 heapdump 导致? 使用 hprof: heap dump 关键词过滤,在d…...

COLA学习之环境搭建(三)
小伙伴们,你们好,我是老寇,上一节,我们学习了COLA代码规范,继续跟老寇学习COLA环境搭建 首先,打开GitHub,搜索 COLA 请给这个COLA项目点个Star,养成好习惯,然后Fork到自…...

CSS输入框动态伸缩动效
前言 下面我们将会做出如下图输入框样式,并且附上组件代码,有特殊需求的可以自行优化同理,下拉框的话只要把el-input标签修改掉即可 MyInput组件 <template><div class"my-input" click.stop"showInput !showInput…...

hbuilder 安卓app手机调试中基座如何设置
app端使用基座 手机在线预览功能 1.点击运行 2.点击运行到手机或者模拟器 3.制作自定义调试基座 4.先生成证书【可以看我上一篇文档写的有】,点击打包 5.打包出android自定义调试基座【android_debug.apk】,【就跟app打包一样需要等个几分钟】 6.点击运行到手…...

探索视觉与语言模型的可扩展性
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
sock_recvmsg函数
sock_recvmsg 是一个在 Linux 内核中用于处理接收网络数据的函数。它通常与套接字 (socket) 操作相关,特别是在网络协议栈中用于处理从网络中接收到的数据。这个函数是内核的一部分,提供了一种机制把接收到的数据从网络协议栈转移到用户空间,或者在内核内进一步处理。 以下是…...

HCIA笔记8--DHCP、Telnet协议
1. DHCP介绍 对于主机的网络进行手动配置,在小规模的网络中还是可以运作的,但大规模网络是无力应对的。因此就有了DHCP协议来自动管理主机网络的配置。 DHCP(Dynamic Host Configuration Protocol): 动态主机配置协议,主要需要配置的参数有…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
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 开发者设计的强大库ÿ…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...