Redis缓存穿透、缓存击穿与缓存雪崩的详细讲解和案例示范

news2024/9/25 23:18:10

在高并发的电商交易系统中,Redis缓存的使用可以极大地提高系统的性能。然而,缓存机制也面临着一些挑战,尤其是缓存穿透、缓存击穿和缓存雪崩问题。这些问题如果处理不当,可能导致系统的性能大幅下降,甚至出现系统崩溃的情况。本文将详细介绍这些问题及其解决方案,并结合电商交易系统的案例进行示范,提供相应的代码示例。


第一章:Redis缓存穿透

1.1 缓存穿透的定义

缓存穿透是指用户请求的数据在缓存中不存在,并且在数据库中也不存在。由于缓存未命中,每次请求都会直接访问数据库,导致数据库压力骤增,最终可能导致系统崩溃。

1.2 缓存穿透的原因
  1. 用户恶意攻击:攻击者通过大量请求不存在的数据来绕过缓存,直接攻击数据库。
  2. 未正确设置缓存:对于数据库中不存在的值,未设置空缓存,导致每次查询都穿透到数据库。
1.3 解决方案
1.3.1 使用布隆过滤器

布隆过滤器是一种概率型数据结构,能够高效判断一个元素是否在一个集合中存在。布隆过滤器通过多个哈希函数将数据映射到位数组中,以达到快速判断的目的。虽然布隆过滤器存在一定的误判率,但它可以有效防止缓存穿透。

实现代码示例
public class BloomFilterService {
    private BloomFilter<String> bloomFilter;

    public BloomFilterService(int expectedInsertions, double fpp) {
        bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp);
    }

    public void addToFilter(String key) {
        bloomFilter.put(key);
    }

    public boolean mightContain(String key) {
        return bloomFilter.mightContain(key);
    }
}
1.3.2 缓存空对象

对于数据库中不存在的数据,将空对象缓存起来,并设置较短的过期时间。这样可以避免大量查询穿透到数据库。

实现代码示例
public String getProductInfo(String productId) {
    String cacheKey = "product:" + productId;
    String productInfo = redisTemplate.opsForValue().get(cacheKey);

    if (productInfo != null) {
        return productInfo;
    }

    // 查询数据库
    Product product = productRepository.findById(productId);
    if (product == null) {
        // 数据库中不存在,缓存空值
        redisTemplate.opsForValue().set(cacheKey, "", 60, TimeUnit.SECONDS);
        return null;
    }

    // 存在,缓存数据
    redisTemplate.opsForValue().set(cacheKey, product.toString(), 10, TimeUnit.MINUTES);
    return product.toString();
}
1.3.3 图示

在这里插入图片描述

1.4 电商系统中的案例

在电商系统中,用户请求某个商品详情,而该商品可能已经下架或者从未存在。在这种情况下,通过使用布隆过滤器和缓存空对象,能够有效防止系统缓存穿透。


第二章:Redis缓存击穿

2.1 缓存击穿的定义

缓存击穿是指缓存中某个热点数据在到期失效的瞬间,有大量的并发请求同时访问该数据,由于缓存失效,这些请求都会穿透到数据库,导致数据库负载剧增,甚至可能导致系统崩溃。

2.2 缓存击穿的原因
  1. 高并发访问:某些热点数据由于频繁访问,可能在缓存失效的瞬间引发大量并发请求直接访问数据库。
  2. 缓存未提前续期:如果没有在缓存即将失效时提前续期,可能导致缓存击穿。
2.3 解决方案
2.3.1 使用互斥锁(分布式锁)

在高并发场景下,当缓存失效时,只有一个请求能够获取锁,其他请求需要等待,直到锁释放后才能访问数据库并更新缓存。

实现代码示例
public String getProductInfo(String productId) {
    String cacheKey = "product:" + productId;
    String productInfo = redisTemplate.opsForValue().get(cacheKey);

    if (productInfo != null) {
        return productInfo;
    }

    // 获取分布式锁
    String lockKey = "lock:product:" + productId;
    boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS);

    if (isLock) {
        try {
            // 查询数据库并更新缓存
            Product product = productRepository.findById(productId);
            redisTemplate.opsForValue().set(cacheKey, product.toString(), 10, TimeUnit.MINUTES);
            return product.toString();
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    } else {
        // 如果没有获取到锁,稍后重试
        Thread.sleep(100);
        return getProductInfo(productId);
    }
}
2.3.2 提前续期

在缓存即将失效时,提前续期缓存数据,以防止缓存失效瞬间的大量并发请求击穿缓存。

实现代码示例
@Scheduled(fixedRate = 5000)
public void refreshHotKeys() {
    List<String> hotKeys = getHotKeys(); // 获取热点数据的缓存Key
    for (String key : hotKeys) {
        redisTemplate.expire(key, 10, TimeUnit.MINUTES); // 重新设置过期时间
    }
}
2.3.3 图示

在这里插入图片描述

2.4 电商系统中的案例

在电商系统中,某些热门商品如秒杀商品会被频繁访问。在缓存失效时,通过分布式锁和提前续期策略,可以有效防止缓存击穿问题。


第三章:Redis缓存雪崩

3.1 缓存雪崩的定义

缓存雪崩是指大量缓存数据在同一时间失效,导致大量请求同时穿透到数据库,造成数据库压力过大,可能导致系统不可用。

3.2 缓存雪崩的原因
  1. 缓存集中失效:由于缓存数据的过期时间设定不合理,大量缓存数据在同一时间失效,导致请求集中穿透到数据库。
  2. 系统重启:如果Redis服务因故障重启,可能导致大量缓存数据失效。
3.3 解决方案
3.3.1 缓存数据过期时间设置为随机值

通过为缓存数据设置随机的过期时间,避免大量缓存数据在同一时间失效。

实现代码示例
public void cacheProductInfo(String productId, String productInfo) {
    int expireTime = 10 + new Random().nextInt(5); // 随机生成10-15分钟的过期时间
    String cacheKey = "product:" + productId;
    redisTemplate.opsForValue().set(cacheKey, productInfo, expireTime, TimeUnit.MINUTES);
}
3.3.2 预热缓存

在系统上线或重启时,提前将热点数据加载到缓存中,防止大量请求瞬间击穿缓存。

实现代码示例
public void cachePreheat() {
    List<String> hotProductIds = productRepository.getHotProductIds();
    for (String productId : hotProductIds) {
        String productInfo = productRepository.findById(productId).toString();
        redisTemplate.opsForValue().set("product:" + productId, productInfo, 10, TimeUnit.MINUTES);
    }
}
3.3.3 图示

在这里插入图片描述

第四章:总结

通过本文的详细讲解,我们对Redis缓存穿透、缓存击穿和缓存雪崩三大问题有了深入的理解。我们使用布隆过滤器和缓存空对象来防止缓存穿透,通过分布式锁和提前续期防止缓存击穿,并通过设置随机过期时间和缓存预热来防止缓存雪崩。每种问题都有其特定的解决方案,通过这些解决方案,我们能够有效提高系统的稳定性和性能。

通过图示,我们直观地展示了这些解决方案的实现过程,这有助于更好地理解和应用这些技术。在实际的电商系统中,合理运用这些技术能够极大提升系统的抗压能力,确保在高并发场景下的稳定性。

希望这篇文章能为您在实际项目中处理Redis缓存问题提供有益的参考。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt】Spacer

Spacer 在使用布局管理的时候&#xff0c;如果需要在控件之间添加一段空白&#xff0c;就可以使用QSpacerItem来表示。 核心属性 属性说明 width 宽度 height ⾼度 hData ⽔平⽅向的 sizePolicy QSizePolicy::Ignored : 忽略控件的尺⼨&#xff0c;不对布局产⽣影响。 QS…

找搭子神器有哪些?盘点找搭子软件排行榜前十名

现在找搭子文化越来越流行&#xff0c;主打一个“恰到好处的陪伴”&#xff0c;深受年轻人喜爱。以下是一些值得推荐的找搭子软件&#xff1a; 1. 咕哇小程序&#xff1a;特点&#xff1a;这是一个实名制的找搭子交友平台&#xff0c;因此相对较为纯粹&#xff0c;骗子较少。用…

【FreeRTOS】使用互斥量解决优先级反转

目录 0 前言0 引言1 互斥量_领导临时提拔你(解决优先级反转)2 怎么使用互斥量3 bug4 总结 0 前言 学习视频&#xff1a; 【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS&#xff08;FreeRTOS教程 基于STM32&#xff0c;以实际项目为导向&#xff09;】 【精准空降到 08:…

什么是无效营养?无效营养对健康和身材的影响

在追求健康饮食和匀称身材的道路上&#xff0c;我们往往被各种营养概念包围&#xff0c;一不小心就会陷入“无效营养”的陷阱。那么&#xff0c;什么是无效营养&#xff1f;简单来说&#xff0c;无效营养‌&#xff1a;指的是摄入的营养物质不能被身体利用&#xff0c;或者摄入…

数据库与缓存一致性的解决方案

数据库和缓存的数据一致性问题一直是老生常谈的话题了&#xff0c;它不仅在面试中十分常见&#xff0c;而且在实际开发中也是需要加以考量的因素。借着难得的空暇时光&#xff08;其实是晚上不太想写代码&#xff09;&#xff0c;笔者今天想和大家简单讨论一下&#xff0c;数据…

Vue实现步骤条(el-step)+Popover弹出框

1、实现效果 hover到每一个步骤条上时&#xff0c;如果当前有未完成情况&#xff08;unFinishedMe不为空&#xff09;&#xff0c;就使用popover显示出来&#xff0c;如果没有hover时就不显示 2、实现思路 循环app信息列表显示多个进度条 使用el-steps 循环步骤列表&#xf…

LeetCode的高频SQL50题(基础版)学习笔记

题目在此网站 https://leetcode.cn/ 查询 # Write your MySQL query statement below select product_id from products where low_fats like Y and recyclable like Y;# Write your MySQL query statement below select name from customer where referee_id !2 or referee_i…

Java笔试面试题AI答之面向对象(7)

文章目录 37. Java成员变量与局部变量的区别有哪些?38. Java 创建一个对象用什么运算符? 对象实体与对象引用有何不同?对象实体与对象引用的不同示例 39. 类的构造方法的作用是什么? 若一个类没有声明构造方法&#xff0c;该程序能正确执行吗? 为什么?40. Java构造方法有…

服了!DELETE 同一行记录也会造成死锁---图文解析

服了&#xff01;DELETE 同一行记录也会造成死锁&#xff01;&#xff01; 作者&#xff1a;转转技术团队 链接&#xff1a;https://juejin.cn/post/7387227689319563290 来源&#xff1a;稀土掘金 MySQL 锁回顾 共享锁 使用共享锁&#xff08;Shared Lock&#xff09;时&am…

模糊视频一键变清晰,从此告别模糊不清的画质

话不多说&#xff0c;咱们直入主题。你是不是有比较模糊的视频&#xff0c;比如老视频&#xff0c;老电影和监控视频&#xff0c;对了&#xff0c;还有日本土特产&#xff08;懂的都懂&#xff09;&#xff0c;模糊的视频看起是不是很不舒服&#xff0c;长期久了还会影响视力影…

这些可视化Python库非常强!

介绍的大体流程是&#xff1a;库名、类型、github star、功能、使用方法、案例、学习资料。 第一部分&#xff1a;数据可视化 pyecharts 类型&#xff1a;可视化图表设计 GitHub Star &#xff1a;5985 功能&#xff1a; 简洁的 API 设计&#xff0c;使用如丝滑般流畅&am…

LLM(二):Prompt

一&#xff0c;什么是Prompt 在人工智能领域&#xff0c;Prompt指的是用户给大型语言模型发出的指令。作用是引导模型生成符合预期主题或内容的文本&#xff0c;从而控制生成结果的方向和内容。 大模型是根据用户提出的问题来输出下文&#xff0c;所以用户提出的问题的质量也…

三种智能指针

智能指针 new和delete 1:new初始化 new未初始化值 int *p new int;//p值未定义string *str new string;//为空串&#xff0c;调用string默认构造函数new 初始化值 int *p new int(100);string *str new string(6,a);//aaaaaavector类型指针 vector<int> *p new v…

pikepdf:一个实用的PDF文件处理Python库

我是东哥&#xff0c;今天给大家介绍一个实用的Python库——pikepdf&#xff0c;它能让你像操作文本文件一样轻松地处理PDF&#xff0c;无论是读取、修改还是保存&#xff0c;都能迎刃而解。 基本介绍 pikepdf是一个基于Python的库&#xff0c;它允许开发者轻松地读取、写入和…

第四课,接收键盘输入

一&#xff0c;关于基本框架中头文件的作用 头文件就是一个工具箱&#xff0c;C中有很多工具&#xff0c;我们最熟悉的cout就是其中之一 引入头文件&#xff1a;如果你想在你的代码中使用工具箱里的工具&#xff0c;C会很大方的让你用&#xff0c;但前提是你必须在本页代码的最…

为什么 CNC 加工会产生毛刺?

在现代机械加工领域&#xff0c;CNC(计算机数控)加工以其高精度、高效率的特点被广泛应用。然而&#xff0c;在 CNC 加工过程中&#xff0c;毛刺的产生常常是一个令人困扰的问题。时利和将解析为什么 CNC 加工会产生毛刺呢? 一、刀具磨损 刀具在长时间的使用过程中会逐渐磨损。…

如何一步快速去除黑神话悟空图片上的文字?一招教会你

设计师朋友们&#xff0c;如果老板让你用去除海报上的文字&#xff0c;你会怎么做&#xff1f; 用PS的内容识别填充&#xff0c;图片就会变模糊再精修简直太麻烦啦&#xff01; 还好我最近找到一个图片处理神器&#xff0c;一键就能P去图片的文字&#xff01;简单又高效&…

day04-面向对象-常用API时间Arrays

一、常用API 1.1 StringBuilder类 StringBuilder类代表可变的字符串对象&#xff0c;理解为一个操作字符串的容器相对于String来说,比本来的操作效率更高 ​ StringBuilder常用方法public StringBuilder(): 构建一个空的可变字符串对象public StringBuilder(String str): 构建…

vue3中vite基于vite-plugin-html的多入口打包

先看打包效果 1、安装vite-plugin-html 2、配置多个入口 每个入口都要有模板(index.html、App.vue、main.js复制一份&#xff0c;根据实际需求调整三个文件) 3、配置vite.config.js 4、代码片段 import { createHtmlPlugin } from vite-plugin-htmlconst htmlParams {minif…

关于springboot的Rest请求映射处理的源码分析(一)

我们在开发中很常见的一种方式是通过请求的类型&#xff0c;也就是restful风格来区别我们的请求接口。 通过请求类型的不同(GET POST PUT DELETE) 来区分即便是请求路径相同的请求。 但是他的底层是如何支持的呢&#xff0c;明明我请求路径都是/user。就因为类型不同就能区分到…