缓存穿透,缓存击穿,缓存雪崩

news2024/11/15 16:56:38

目录

介绍

缓存穿透

缓存击穿

缓存雪崩

原因

影响

解决方案

缓存穿透

防止缓存穿透->空值缓存案例

缓存击穿

使用互斥锁解决缓存击穿


                                                    介绍

   缓存穿透

定义:缓存穿透是指用户查询数据,缓存和数据库中都不存在该数据(一般是发起恶意的查询,试图击穿缓存,直接查询数据库),这时用户每次查询都会直接打到数据库上,而数据库中也没有该数据,如果用户不断发起这样的请求,数据库压力会非常大,甚至可能拖垮数据库。

解决方案

  1. 布隆过滤器(Bloom Filter):布隆过滤器可以快速判断一个元素是否在一个集合中,但是会有一定的误判率。在数据放入缓存之前,先使用布隆过滤器判断数据是否存在,如果不存在则直接返回,不进行数据库查询。
  2. 空值缓存:对于不存在的数据,也在缓存中存放一个空值(或者一个特殊标记),这样下次查询相同的数据时,可以直接返回缓存中的空值,避免了对数据库的查询。但是这种方法需要设置合理的过期时间,避免数据长时间不更新。
  3. 请求限流:对查询请求进行限流,限制查询频率,防止恶意查询。

   缓存击穿

定义:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

解决方案

  1. 设置热点数据永不过期,即逻辑缓存:对于一些热点数据,可以将其设置为永不过期,从而避免缓存击穿问题。但是这种方法会占用较多的缓存空间,需要谨慎使用。
  2. 加互斥锁:在查询数据库之前,先尝试获取一个分布式锁,如果获取到锁,则查询数据库并更新缓存;如果未获取到锁,则等待一段时间后重试。这样可以保证同一时间只有一个请求去查询数据库,从而减轻数据库压力。
  3. 双缓存策略:使用两个缓存,一个缓存的过期时间较短,用于应对大部分请求;另一个缓存的过期时间较长,用于应对缓存击穿的情况。当短缓存过期时,先从长缓存中获取数据,然后再去查询数据库并更新两个缓存。

   缓存雪崩

缓存雪崩是指在缓存中存储的大量数据同时失效或过期,导致缓存系统无法承载大量请求压力,造成服务宕机甚至瘫痪的情况。这种情况下,大量的请求会直接涌入数据库,导致数据库崩溃或响应缓慢,影响应用程序的正常使用。

原因

缓存雪崩通常由于以下几个原因引起:

  1. 缓存过期策略:如果大量的缓存数据被设置为相同的过期时间,那么在这些数据同时过期时,就会引发缓存雪崩。
  2. 缓存服务器故障:当缓存服务器(如Redis)宕机或网络中断时,缓存服务将不可用,所有请求都会直接打到数据库上。
  3. 大量突发请求:在特定时间段内,如果请求量激增且超出缓存系统的处理能力,也可能导致缓存雪崩。

 影响

缓存雪崩的影响是灾难性的,主要包括以下几个方面:

  1. 数据库负载过高:大量的请求直接打到数据库上,导致数据库负载急剧增加,响应时间延长。
  2. 服务不可用:在极端情况下,数据库可能因为压力过大而崩溃,导致整个服务不可用。
  3. 性能瓶颈:数据库成为瓶颈,系统整体处理能力下降,用户体验受到严重影响。
  4. 连锁反应:由于服务间的依赖关系,缓存雪崩可能导致多个服务相继瘫痪,形成连锁反应。

 解决方案

为了防范和应对缓存雪崩,可以采取以下策略:

  1. 设置随机过期时间:为不同的缓存数据设置随机的过期时间,避免大量数据同时过期。
  2. 限流和熔断:当检测到缓存系统压力过大时,通过限流组件限制请求量,并在必要时采取熔断措施,如返回默认数据或静态页面。
  3. 主动更新缓存:在缓存失效前,主动提前重新加载数据至缓存,以减轻数据库压力。
  4. 加锁机制:当缓存失效后,通过加锁机制控制只有一个线程负责从数据库加载数据并回填缓存,其他线程等待或返回旧数据。
  5. 部署缓存集群:部署Redis Sentinel或Cluster集群等缓存集群方案,确保单点故障时能自动切换到其他节点。
  6. 使用布隆过滤器:在缓存之前使用布隆过滤器判断请求的键是否存在,减少对数据库的无效访问。

                                         缓存穿透

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。给数据库带来巨大压力

常见的解决方案有两种:

缓存空对象

  • 优点:实现简单,维护方便
  • 缺点:额外的内存消耗    可能造成短期的不一致

布隆过滤

优点:内存占用较少,没有多余key

缺点:实现复杂      存在误判可能

还有的解决方案:主动性的方案

  • 增强id的复杂性,避免被猜测id规律
  • 做好数据的基础格式校验
  • 加强用户权限校验
  • 做好热点参数的限流

  防止缓存穿透->空值缓存案例

public Shop queryWithPassThrough(Long id) {

    String key = CACHE_SHOP_KEY + id;
    Map<Object, Object> shopFromCache = stringRedisTemplate.opsForHash().entries(key);
    System.out.println("Redis: " + shopFromCache);

    // 检查 Redis 返回的 shopFromCache 是否为 null
    if (shopFromCache != null && !shopFromCache.isEmpty()) {
        // 缓存中有值,尝试反序列化 Shop 对象
        Shop shopFromCacheBean = BeanUtil.fillBeanWithMap(shopFromCache, new Shop(), false);
        return shopFromCacheBean;
    }

    // Redis 中不存在,从数据库中查询
    Shop shopFromDb = baseMapper.selectById(id);

    // 数据库中也不存在,则缓存空值以防缓存穿透
    if (ObjectUtil.isNull(shopFromDb)) {
        stringRedisTemplate.opsForHash().put(key, CACHE_SHOP_EMPTY_KEY, CACHE_SHOP_EMPTY_VALUE); // 使用特殊标记表示空值
        stringRedisTemplate.expire(key, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return null;
    }

    // 将 Shop 对象转换为 Map 并写入 Redis
    Map<String, Object> shopMap = BeanUtil.beanToMap(shopFromDb, new HashMap<>(),
            CopyOptions.create().ignoreNullValue()
                    .setFieldValueEditor((fieldName, fieldValue) -> fieldValue != null ? fieldValue.toString() : null));
    stringRedisTemplate.opsForHash().putAll(key, shopMap);
    // 设置超时时间
    stringRedisTemplate.expire(key, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);

    return shopFromDb;
}


                 缓存击穿                       

缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击

解决方案 优点 缺点

互斥锁 没有额外的内存消耗 线程需要等待,性能受影响

保证一致性 可能有死锁风险

实现简单

逻辑过期 线程无需等待,性能较好 不保证一致性

有额外内存消耗

实现复杂

使用互斥锁解决缓存击穿

@Override
    public Result queryById(Long id) throws InterruptedException {
    // 缓存穿透
//            queryWithPassThrough(id);

    // 互斥锁解决缓存击穿
        Shop shop = queryWithMutex(id);
        if (shop==null) {
            return Result.fail("商品不存在");
        }

        return Result.ok(shop);
    }

    public Shop queryWithMutex(Long id) throws InterruptedException {

        String key = CACHE_SHOP_KEY + id;
        Map<Object, Object> shopFromCache = stringRedisTemplate.opsForHash().entries(key);
        System.out.println("Redis: " + shopFromCache);

        // 检查 Redis 返回的 shopFromCache 是否为 null
        if (shopFromCache != null && !shopFromCache.isEmpty()) {
            // 缓存中有值,尝试反序列化 Shop 对象
            Shop shopFromCacheBean = BeanUtil.fillBeanWithMap(shopFromCache, new Shop(), false);
            return shopFromCacheBean;
        }

        // Redis 中不存在,从数据库中查询
        // 4.实现缓存重建
        // 4.1获取互斥锁
        // 4.2获取失败,休眠然后重试
        // 4.3获取成功,返回数据,释放锁
        Shop shopFromDb=null;
        try {
            boolean isLock = tryLock(LOCK_SHOP_KEY + id);
            if (!isLock) {
            // 拿不到锁,休眠,然后递归不断尝试
            // 这个返回结果会是在redis里拿到的数据
                Thread.sleep(50);
                return queryWithMutex(id);
            }
                // 拿到互斥锁,查询数据库,重建缓存
            shopFromDb= baseMapper.selectById(id);
//            模拟重建延迟
            Thread.sleep(200);

                // 数据库中也不存在,则缓存空值以防缓存穿透

                    stringRedisTemplate.opsForHash().put(key, CACHE_SHOP_EMPTY_KEY, CACHE_SHOP_EMPTY_VALUE); // 使用特殊标记表示空值
                    stringRedisTemplate.expire(key, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
                    return null;
                }

                // 将 Shop 对象转换为 Map 并写入 Redis
                Map<String, Object> shopMap = BeanUtil.beanToMap(shopFromDb, new HashMap<>(),
                        CopyOptions.create().ignoreNullValue()
                                .setFieldValueEditor((fieldName, fieldValue) -> fieldValue != null ? fieldValue.toString() : null));
                stringRedisTemplate.opsForHash().putAll(key, shopMap);
                // 设置超时时间
                stringRedisTemplate.expire(key, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
        // 释放锁
            unLock(LOCK_SHOP_KEY + id);
        }
        return shopFromDb;
    }
public   boolean tryLock(String key) {
    Boolean b = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(b);
}

public   void unLock(String key) {
   stringRedisTemplate.delete(key);
}

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

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

相关文章

Nginx笔记(一)

一、Nginx简介 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器 [13]&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;公开版本1.…

maven项目容器化运行之3-优雅的利用Jenkins和maven使用docker插件调用远程docker构建服务并在1Panel中运行

一.背景 在《maven项目容器化运行之1》中&#xff0c;我们开启了1Panel环境中docker构建服务给到了局域网。在《maven项目容器化运行之2》中&#xff0c;我们基本实现了maven工程创建、远程调用docker构建镜像、在1Panel选择镜像运行容器三大步骤。 但是&#xff0c;存在一个问…

影视剪辑(自媒体)工具下载地址

一、资源下载网址 片库&#xff08;资源较全&#xff09;&#xff1a; 中国大陆线路&#xff1a;https://www.btnull.nuhttps://www.btnull.nuhttps://www.btnull.inhttps://www.btnull.in 其他网址&#xff1a; https://www.btnull.comhttps://www.btnull.com…

Oracle export 时expdp 表顺序每次不同的

同样的expdp 200张表&#xff0c;expdp 的命令一样&#xff0c;但是export log出来的表顺序也是不同的。 expdp parallel 4 impdp parallel 8 也是可以 File Size&#xff08;MB&#xff09;Export ElapsedImport ElapsedAKA2865MB0:04:230:20:02imp disable constraint paral…

畅游时空|虚拟世界初体验,元宇宙游戏如何开发?

在元宇宙中&#xff0c;用户可以通过虚拟身份进行互动、社交、工作和娱乐&#xff0c;体验与现实世界平行的生活和活动。元宇宙不仅仅是一个虚拟空间&#xff0c;更是一个融合了虚拟和现实的生态系统&#xff0c;具有巨大的发展潜力和应用前景。 在不断发展的数字环境中&#x…

MySQL的账户管理

目录 1 密码策略 1.1 查看数据库当前密码策略&#xff1a; 1.2 查看密码设置策略 1.3 密码强度检查等级解释&#xff08;validate_password.policy&#xff09; 2 新建登录账户 3 账户授权 3.1 赋权原则 3.2 常见的用户权限 3.3 查看权限 3.4 赋权语法 4 实例 4.1 示例1&#x…

教学用MQTT工具的思考

前言 我在《智能物联网应用设计》课程中有个实验就是学习MQTT的使用&#xff0c;目前使用的是以下几个工具&#xff1a; 客户端使用MQTTX本地服务器使用的是mosquitto 云端服务器采用的巴法云协议分析软件采用的是Wireshark 这里Wireshark基本没有啥可以替代的&#xff0c;而…

【React】箭头函数:现代 JavaScript 的高效编程方式

文章目录 一、箭头函数的基本语法二、箭头函数的特性三、在 React 中的常见用法四、最佳实践 在现代 JavaScript 中&#xff0c;箭头函数&#xff08;Arrow Functions&#xff09;是一种简洁的函数表达方式&#xff0c;并且在 React 开发中非常常见。箭头函数不仅简化了函数的语…

Adobe国际认证详解-ae视频剪辑

AE视频剪辑&#xff0c;即After Effects视频剪辑&#xff0c;是数字媒体时代的一项重要技能。AE不仅提供了丰富的特效和动画功能&#xff0c;还让视频剪辑变得更加高效和有趣。对于想要在职场上脱颖而出、拓宽就业机遇的人来说&#xff0c;掌握AE视频剪辑技能无疑是一个明智的选…

添加动态云层

<template> <div class"topbox"> xx卫星管理 </div> <div class"selectbox"> <div class"title"> 卫星列表 </div> <el-table :data"tableData" style"width: 100%;height:230px;" …

基于 HTML+ECharts 实现的大数据可视化平台模板(含源码)

构建大数据可视化平台模板&#xff1a;基于 HTML 和 ECharts 的实现 大数据的可视化对于企业决策、市场分析和业务洞察至关重要。通过直观的数据展示&#xff0c;团队可以快速理解复杂的数据模式&#xff0c;发现潜在的业务机会。本文将详细介绍如何利用 HTML 和 ECharts 实现一…

配置SSH公钥互信

目录 第一台主机&#xff1a;servera&#xff08;172.25.250.101&#xff09; 第一步&#xff1a;查看 . ssh目录下面是否为空 第二步&#xff1a;输入命令ssh-keygen 第三步&#xff1a; 再看查看一下. ssh目录 第四步&#xff1a; 输入命令 ssh-copy-id root172.25.250…

KubeSphere核心实战_kubesphere全功能安装_启用kubesphere的热插拔插件---分布式云原生部署架构搭建037

然后我们开始安装kubesphere,首先进入官网点击kubernetes安装 可以看到对应的,条件说kubernetes要在1.20.x以上,我们的是 1.20.9,然后cpu硬件满足,然后,默认存储类型,上一节我们安装好了 然后就可以开始,去下载两个配置文件可以看到上面的两个配置文件 这两个文件,上面是直接…

【React】package.json 文件详解

文章目录 一、package.json 文件的基本结构二、package.json 文件的关键字段1. name 和 version2. description3. main4. scripts5. dependencies 和 devDependencies6. repository7. keywords8. author 和 license9. bugs 和 homepage 三、package.json 文件的高级配置1. 配置…

《Java初阶数据结构》----4.<线性表---Stack栈和Queue队列>

前言 大家好&#xff0c;我目前在学习java。之前也学了一段时间&#xff0c;但是没有发布博客。时间过的真的很快。我会利用好这个暑假&#xff0c;来复习之前学过的内容&#xff0c;并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区…

<数据集>抽烟识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;4860张 标注数量(xml文件个数)&#xff1a;4860 标注数量(txt文件个数)&#xff1a;4860 标注类别数&#xff1a;1 标注类别名称&#xff1a;[smoking] 使用标注工具&#xff1a;labelImg 标注规则&#xff1a;对…

高三了,无计算机基础能学计算机吗?

高三阶段学习计算机编程是完全可行的&#xff0c;即使你没有任何计算机基础。我收集制作一份C语言学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向&#xff0c;包括了编程教学&#xff0c;数据处理&#xff0c;通信处理&#xff0c;技…

mysql面试(五)

前言 本章节从数据页的具体结构&#xff0c;分析到如何生成索引&#xff0c;如何构成B树的索引结构。 以及什么是聚簇索引&#xff0c;什么是联合索引 InnoDB数据结构 行数据 我看各种文档中有好多记录数据结构的&#xff0c;但是这些都是看完就忘的东西。在这里详细讲也没…

【进阶篇-Day12:JAVA中stream流、File类的介绍】

目录 1、stream流1.1 stream流的作用1.2 stream流的思想1.3 获取stream流对象1.4 stream流中间操作方法1.5 stream流终结操作方法1.6 stream收集操作1.7 stream的综合案例 2、File类2.1 File类创建文件对象2.2 File类的常用方法2.3 File类的创建和删除方法2.4 File类的遍历方法…

volatile与JMM

目录 volatile特点volatile内存语义volatile为什么可以保证可见性和有序性呢&#xff1f;内存屏障屏障分类粗分两种屏障细分四种屏障 volatile特性案例说明如何正确使用volatile总结 volatile特点 被volatile修饰的变量有2大特点&#xff1a;可见性和有序性&#xff08;禁重排…