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

Redis 击穿、穿透与雪崩:深度解析与应对策略

在使用 Redis 作为缓存的系统架构中,缓存击穿、穿透和雪崩是三个常见且可能对系统性能产生严重影响的问题。深入理解这些问题并掌握有效的应对策略对于构建稳定、高效的系统至关重要。

一、缓存击穿

(一)问题描述

缓存击穿是指一个热点 key 在缓存中突然过期,而此时大量并发请求同时访问这个 key,由于缓存中不存在该数据,这些请求会直接穿透到数据库查询数据,并且在查询到数据后重新将数据写入缓存。在高并发场景下,数据库可能瞬间承受巨大的压力,甚至导致数据库服务不可用。

例如,在一个电商系统中,某个热门商品的详情信息被缓存了。当缓存过期时,恰好有大量用户同时点击查看该商品详情,这些请求就会同时涌向数据库获取商品数据,可能使数据库负载急剧上升。

(二)处理方法

  1. 设置热点数据永不过期:对于一些极端热点且更新频率极低的数据,可以设置其在缓存中永不过期。这样可以避免因缓存过期导致的击穿问题,但需要注意数据一致性的维护,当数据有更新时,要及时更新缓存中的数据。
  2. 使用互斥锁:当缓存中不存在热点 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 查询请求,这些请求都会直接访问数据库,数据库不断进行无效查询操作,浪费大量资源。

(二)处理方法

  1. 缓存空对象:当查询数据库未找到数据时,将空对象缓存起来,并设置一个较短的过期时间。这样后续相同的查询请求就可以直接从缓存中获取空结果,避免再次查询数据库。示例代码如下:

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 服务突然不可用,导致大量请求直接访问数据库,数据库压力瞬间剧增,可能导致数据库服务崩溃,进而影响整个系统的正常运行。

例如,在一个社交系统中,很多用户的动态数据都被缓存了,并且设置了相同的过期时间。当这些缓存同时过期时,大量用户的请求就会同时涌向数据库获取动态数据,使数据库不堪重负。

(二)处理方法

  1. 设置缓存过期时间随机化:将缓存的过期时间设置为一个随机值,避免大量缓存同时过期。例如,可以在原本设置的过期时间基础上,加上一个随机的时间偏移量,使不同 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);
}

  1. 使用缓存预热:在系统启动或缓存服务重启时,提前将一些热点数据加载到缓存中,避免在用户请求高峰时因缓存未命中而导致大量请求穿透到数据库。可以在系统启动时编写一个数据加载脚本,从数据库中查询热点数据并写入缓存。
  2. 搭建高可用的 Redis 集群:使用 Redis 集群可以提高 Redis 的可用性和容错性。当部分节点出现故障时,集群中的其他节点可以继续提供服务,减少因 Redis 服务不可用导致的雪崩影响。常见的 Redis 集群方案有主从复制、哨兵模式和 Redis Cluster 等。例如,在主从复制模式下,主节点负责写操作,从节点负责读操作,从节点会定期从主节点同步数据。当主节点故障时,可以手动或自动将从节点提升为主节点,继续提供服务。
  3. 限流与降级:在系统中引入限流和降级机制,当发现缓存雪崩发生且数据库压力过大时,对部分非核心业务的请求进行限流,只允许一定数量的请求通过,或者直接对这些非核心业务进行降级处理,返回默认数据或提示信息,以保护数据库和整个系统的稳定性。例如,可以使用令牌桶算法或漏桶算法进行限流,在代码中根据业务的重要性设置不同的限流阈值和降级策略。

综上所述,缓存击穿、穿透和雪崩是 Redis 缓存使用过程中需要重点关注的问题。通过合理设置缓存过期时间、使用互斥锁、缓存空对象、布隆过滤器、缓存预热、搭建高可用集群以及限流降级等多种技术手段,可以有效地预防和应对这些问题,提高系统的性能、稳定性和可靠性,为用户提供更好的服务体验。在实际的系统开发和运维中,需要根据系统的业务特点、数据规模和并发量等因素,综合运用这些策略,不断优化系统架构和缓存策略。

相关文章:

Redis 击穿、穿透与雪崩:深度解析与应对策略

在使用 Redis 作为缓存的系统架构中&#xff0c;缓存击穿、穿透和雪崩是三个常见且可能对系统性能产生严重影响的问题。深入理解这些问题并掌握有效的应对策略对于构建稳定、高效的系统至关重要。 一、缓存击穿 &#xff08;一&#xff09;问题描述 缓存击穿是指一个热点 key…...

8086处理器的寻址方式

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

Mask实现裁剪的原理浅析

简单来说&#xff0c;就是Mask会设置继承了MaskableGraphic的组件的Shader属性&#xff0c;进行特定的模板测试 一张普通的Image&#xff0c;当不挂Mask组件时&#xff0c;其默认Shader的模板缓存属性是这样的 当挂载上Mask时&#xff0c;会改变 Stencil ID变成了1&#xff…...

每隔一秒单片机向电脑发送一个16进制递增数据

SCON0x50 SM00 SM11&#xff08;工作方式为方式一&#xff09; REN1允许单片机从电脑接收数据 TB8 RB8 SM2是方式2和方式3直接配置为0 TI为发送中断请求标志位 由硬件配置为1 必须由 软件复位为0&#xff0c;RI为接收中断请求标志位&#xff0c;同理TI UART.c #include &l…...

逆向攻防世界CTF系列56-easy_Maze

逆向攻防世界CTF系列56-easy_Maze 64位无壳&#xff0c;看题目就知道是迷宫问题了 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服务器 | 永久重定向与临时重定向

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 &#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系…...

电压调整电路汇总

目录&#xff1a; 一、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 提供可调节电压版本&#xff0c…...

day28 文件IO及进程线程基础

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

【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 前言 随着无服务计算的兴起和大数据环境中的数据集成需求&#xff0c; 需要使用某些轻量级的服务&#xff0c;来实现一些简单操作。因此Azure Function就成了微软云上的一个必不可少的组成部分。 …...

前端文件下载

这里写自定义目录标题 前端文件下载方法使用a标签使用iframe标签二进制流 前端文件下载方法 使用a标签 /*** 文件下载方法 使用a标签* 存在浏览器下载时&#xff0c;太快的话&#xff0c;会取消上次的下载请求* param {*} href* param {*} filename*/ export function downlo…...

前端成长之路:HTML(3)

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

无人机自动机库的功能与作用!

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

ubuntu 新建脚本shell并增加图标 双击应用实现python运行

1.使用nano创建shell脚本文件 需要在终端窗口中输入“nano”以打开文本编辑器。 nano 在创建脚本文件前&#xff0c;我们要了解脚本文件是如何运行的&#xff1a; 直接运行&#xff1a;直接在终端直接输入需要运行的脚本文件名称&#xff0c;系统或用缺省版本的shell运行脚…...

ANR 分析SOP

遇到ANR问题不要慌&#xff0c;大部分情况下可能是系统or测试手段问题&#xff0c;我们按照如下关键字排查定位 文章目录 1 是否是 heapdump 导致&#xff1f;1.1 dump开始1.2 dump结束 1 是否是 heapdump 导致&#xff1f; 使用 hprof: heap dump 关键词过滤&#xff0c;在d…...

COLA学习之环境搭建(三)

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

CSS输入框动态伸缩动效

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

hbuilder 安卓app手机调试中基座如何设置

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

探索视觉与语言模型的可扩展性

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

sock_recvmsg函数

sock_recvmsg 是一个在 Linux 内核中用于处理接收网络数据的函数。它通常与套接字 (socket) 操作相关,特别是在网络协议栈中用于处理从网络中接收到的数据。这个函数是内核的一部分,提供了一种机制把接收到的数据从网络协议栈转移到用户空间,或者在内核内进一步处理。 以下是…...

HCIA笔记8--DHCP、Telnet协议

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

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇

根据 QYResearch 发布的市场报告显示&#xff0c;全球市场规模预计在 2031 年达到 9848 万美元&#xff0c;2025 - 2031 年期间年复合增长率&#xff08;CAGR&#xff09;为 3.7%。在竞争格局上&#xff0c;市场集中度较高&#xff0c;2024 年全球前十强厂商占据约 74.0% 的市场…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数&#xff0c;单位是赫兹&#xff08;Hz&#xff09;。 60Hz 屏幕&#xff1a;每秒刷新 60 次&#xff0c;每次刷新间隔约 16.67ms 90Hz 屏幕&#xff1a;每秒刷新 90 次&#xff0c;…...

CMS内容管理系统的设计与实现:多站点模式的实现

在一套内容管理系统中&#xff0c;其实有很多站点&#xff0c;比如企业门户网站&#xff0c;产品手册&#xff0c;知识帮助手册等&#xff0c;因此会需要多个站点&#xff0c;甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...