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): 动态主机配置协议,主要需要配置的参数有…...
经典概率题:飞机座位分配问题(LeetCode 1227)超详细解析
一、题目背景与描述这是一道非常经典的概率与逻辑推理面试题,也是 LeetCode 第 1227 题「飞机座位分配概率」。题目描述有 n 位乘客即将登机,飞机正好有 n 个座位。第一位乘客的票丢了,他随机选一个座位坐下。剩下的乘客:如果自己…...
OWASP靶场实战指南:从环境搭建到第一个SQL注入漏洞挖掘(含DVWA通关思路)
OWASP靶场实战指南:从环境搭建到第一个SQL注入漏洞挖掘 网络安全的世界就像一片未知的海洋,而靶场就是我们练习游泳的安全泳池。对于刚入门的新手来说,最大的困扰往往不是缺乏理论知识,而是不知道如何将所学付诸实践。OWASP靶场正…...
CentOS7下SSD性能调优实战:iostat与dd命令的黄金组合
CentOS7下SSD性能调优实战:iostat与dd命令的黄金组合 在当今数据驱动的时代,存储性能往往成为系统瓶颈的关键所在。对于使用CentOS7系统的运维工程师来说,如何充分释放SSD硬件的性能潜力,是一个既具挑战性又充满成就感的技术课题。…...
别再只用Set5了!超分辨率模型训练,这5个开源数据集(DIV2K、Flickr2K等)的实战配置与对比
超分辨率模型训练:5个开源数据集的深度实战指南 在超分辨率研究领域,数据集的选择往往决定了模型性能的上限。许多开发者习惯性地使用Set5、Set14等小型数据集,却忽略了更丰富的数据资源可能带来的性能突破。本文将深入解析DIV2K、Flickr2K、…...
Deformable-DETR环境配置避坑:如何正确设置CUDA_HOME解决ms_deformable_im2col_cuda报错
Deformable-DETR环境配置实战:从CUDA路径排查到高效编译 当你第一次尝试运行Deformable-DETR这个强大的目标检测框架时,是否也遇到了那个令人头疼的报错:"error in ms_deformable_im2col_cuda: no kernel image is available for execut…...
D-Net:动态大内核与特征融合如何革新三维医学影像分割?
1. 为什么医学影像分割需要动态大内核? 医学影像分割就像给CT或MRI照片上的器官、肿瘤画精确边界线。传统方法像用固定倍数的放大镜观察——要么看不清细节(小内核),要么错过整体结构(大内核)。我在处理腹…...
【模型手术室】第七篇:模型量化 —— 从 FP16 到 4-bit 的极限压缩与性能翻倍
专栏进度:07 / 10 (微调实战专题) 大模型默认使用 FP16(16 位浮点数) 存储权重,这意味着每个参数占 2 字节。一个 7B 模型光权重就占 14GB 显存。量化的本质是把这些高精度的数字映射到更小的整数空间(如 INT4…...
VitePress 博客主题定制与美化实战
1. VitePress主题美化的核心思路 很多开发者在使用VitePress搭建博客时,都会遇到一个共同的问题:默认主题虽然简洁,但缺乏个性。我在实际项目中发现,通过CSS变量覆盖、自定义组件和插件扩展这三个维度,可以打造出极具辨…...
利用快马ai快速生成流水线plc控制逻辑原型,无硬件也能验证思路
最近在做一个自动化流水线的小项目,需要设计PLC控制逻辑。传统方式需要先搭建硬件环境才能调试,但通过InsCode(快马)平台的AI辅助,我实现了无硬件环境下的快速原型验证,分享下这个实用经验。 项目背景与需求分析 这个流水线控制系…...
实战复盘-Redis连接数爆满引发的生产事故与优化策略
1. 事故背景:一场由促销活动引发的Redis雪崩 那天凌晨三点,我被一阵急促的电话铃声惊醒。电话那头是值班同事焦急的声音:"所有商品页面都打不开了,订单系统也瘫痪了!"我瞬间清醒,抓起电脑就开始…...
