Redis 缓存穿透、缓存雪崩、缓存击穿

news2024/9/21 11:11:39

缓存穿透

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

常见的解决方案有两种:

        缓存空对象

                 优点:实现简单,维护方便

                缺点: 额外的内存消耗 可能造成短期的不一致(恶意攻击的对象id对应的redis空值缓存失效前成为了新插入的id,造成真实客户只能得到空缓存)

        布隆过滤(类似于位示图0、1代表是否存在)

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

                缺点: 实现复杂 存在误判可能(对于过滤结果:假的一定为假,真的有小概率为假)

 

 缓存穿透的解决方案有哪些?

缓存null值

布隆过滤

增强id的复杂度,避免被猜测id规律

做好数据的基础格式校验

加强用户权限校验

做好热点参数的限流

代码解释,这是一个通用的工具类方法:

public <R,ID> R queryWithPassThrough(
            String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){
        String key=keyPrefix+id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)){
            return JSONUtil.toBean(json,type);
        }
        if (json!=null){
            return null;
        }
        R apply = dbFallback.apply(id);
        if (apply==null){    //生成空值缓存,避免缓存穿透
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);  //对象转Json
            return null;
        }
        this.set(key,JSONUtil.toJsonStr(apply),time, unit);
        return apply;
    }

缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

给不同的Key的TTL添加随机值

利用Redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

缓存击穿

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

常见的解决方案有两种:

互斥锁、逻辑过期

 

基于代码实现互斥锁

public <R,ID> R queryWithMutex(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbrFallback,Long time,TimeUnit unit){
        String key= keyPrefix+id;
        String shopJson= stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(shopJson)){  //命中Redis缓存
            return JSONUtil.toBean(shopJson, type);
        }
        if(shopJson!=null){     //命中空值缓存,代表恶意攻击
            return null;
        }

        if (!tryLock(LOCK_SHOP_KEY+id)) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return queryWithMutex(keyPrefix,id,type,dbrFallback,time,unit);
        }
        String shopJson2= stringRedisTemplate.opsForValue().get(key);
        //doubleCheck
        if (StrUtil.isNotBlank(shopJson2)){  //命中Redis缓存
            unlock(LOCK_SHOP_KEY+id);
            return JSONUtil.toBean(shopJson2, type);
        }
        if(shopJson2!=null){     //命中空值缓存,代表恶意攻击
            unlock(LOCK_SHOP_KEY+id);
            return null;
        }
        R apply = dbrFallback.apply(id);
        if (apply==null){    //生成空值缓存,避免缓存穿透
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);  //对象转Json
            return null;
        }
        this.setWithLogicalExpire(key,JSONUtil.toJsonStr(apply),time,unit);
        unlock(LOCK_SHOP_KEY+id);
        return apply;
    }

基于代码实现逻辑过期 

    public <R,ID> R queryWithLogicalExpire(
            String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){
        String key= CACHE_SHOP_KEY+id;
        String Json= stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(Json)){  //未命中Redis缓存
            return null;
        }
        RedisData redisData = JSONUtil.toBean(Json, RedisData.class);
        R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        if (redisData.getExpireTime().isAfter(LocalDateTime.now())){
            return r;
        }
        if (tryLock(key)){
            String json=stringRedisTemplate.opsForValue().get(key);
            if (StrUtil.isNotBlank(json)){  //命中Redis缓存 二次命中缓存,兜底
                RedisData redisData1 = JSONUtil.toBean(json, RedisData.class);
                if (redisData1.getExpireTime().isAfter(LocalDateTime.now())){
                    unlock(key);
                    return JSONUtil.toBean((JSONObject) redisData1.getData(), type);
                }
            }
            CACHE_REBUILD_EXECUTOR.submit(()->{
                R apply = dbFallback.apply(id);
                this.setWithLogicalExpire(key,apply,time,unit);
                unlock(key);
            });
        }
        return r;
    }

附工具类方法

    public void set(String key, Object value, Long time, TimeUnit util){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,util);
    }
    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit util){
        RedisData redisData=new RedisData();
        redisData.setData(value);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(util.toSeconds(time)));
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));
    }
    //线程池
    public static final ExecutorService CACHE_REBUILD_EXECUTOR =             
    Executors.newFixedThreadPool(10);
    /**
     * 获取互斥锁
     * @param key
     * @return
     */
    public Boolean tryLock(String key){
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

    /**
     * 释放互斥锁
     * @param key
     * @return
     */
    public void unlock(String key){
        stringRedisTemplate.delete(key);
    }

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

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

相关文章

chatGPT写文章一半不写了-如何让chatGPT写完整文章

chatGPT不生成内容的原因有哪些 当ChatGPT不生成内容时&#xff0c;可能有如下原因&#xff1a; 数据限制&#xff1a;ChatGPT的生成能力是建立在其训练数据的基础上的。如果输入的内容领域、主题和题材不在其数据范围内&#xff0c;ChatGPT将无法生成非常有意义和具体的内容。…

图像修补论文阅读:MAT算法笔记

标题&#xff1a;MAT: Mask-Aware Transformer for Large Hole Image Inpainting 会议&#xff1a;CVPR2022 论文地址&#xff1a;https://ieeexplore.ieee.org/document/9879508/ 官方代码&#xff1a;https://github.com/fenglinglwb/MAT 作者单位&#xff1a;香港中文大学、…

第十一讲 常用数据结构之字符串

第二次世界大战促使了现代电子计算机的诞生&#xff0c;世界上的第一台通用电子计算机名叫 ENIAC&#xff08;电子数值积分计算机&#xff09;&#xff0c;诞生于美国的宾夕法尼亚大学&#xff0c;占地167平米&#xff0c;重量约27吨&#xff0c;每秒钟大约能够完成约5000次浮点…

Ansible基础和命令行模块操作

目录 1.Ansible介绍 1.Ansible能做什么&#xff1f; 2.Ansible的特性和原理 2.Ansible部署 3.Ansible命令模块 1.command模块 2.shell模块 3.cron模块 4.user模块 5.group模块 7.file模块 8.hostname 模块 9.ping 模块 10. yum 模块 11.service/systemd 模块 1…

【私有云底层】理解OpenStack核心组件

文章目录 &#x1f479; 关于作者一、Keystone 身份认证服务Keystone 架构工作流程 二、Glance 镜像服务Glance 架构磁盘与容器Glance 工作流程 三、Placement 放置服务Placement 工作流程 四、Nova 计算服务Nova 架构Nova 工作流程 五、Neutron 网络服务Neutron 架构Neutron 支…

jstat命令查看jvm内存情况及GC内存变化

命令格式 jstat [Options] pid [interval] [count] 参数说明&#xff1a; Options&#xff0c;选项&#xff0c;一般使用 -gc、-gccapacity查看gc情况 pid&#xff0c;VM的进程号&#xff0c;即当前运行的java进程号 interval&#xff0c;间隔时间(按该时间频率自动刷新当前内存…

Shell脚本之条件测试、if、case条件测试语句

目录 一、条件测试1.1test命令1.2文件测试1.2.1文件测试常见选项 1.3整数值比较1.4字符串比较1.5逻辑测试 二、if语句2.1单分支结构2.2双分支结构2.3多分支结构 三、case语句 一、条件测试 1.1test命令 测试特定的表达式是否成立&#xff0c;当条件成立&#xff0c;测试语句的…

Android Studio 2021 导出aar到Unity

1,新建一个新工程&#xff0c;创建一个Empty Activity 2.下面的都用默认即可 3.修改工程一些配置 修改setting.gradle maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } maven { url https://maven.aliyu…

【matplotlib】可视化解决方案——如何正确设置轴长度和范围

概述 在 matplotlib 绘图时&#xff0c;往往需要对坐标轴进行设置&#xff0c;默认情况下&#xff0c;每一个绘图的最后都会调用 plt.autoscale() 方法&#xff0c;这个方法的底层是 gca().autoscale(enableenable, axisaxis, tighttight)&#xff0c;本质是调用当前 Axes 对象…

【HDU - 6558】The Moon(概率dp)

ps&#xff1a;初学概率dp&#xff0c;所以 就算是板子也 是看了非常久&#xff0c;好在最后还是学会了qwq… 文章目录 题意思路代码总结 题意 思路 概率dp通常为从能够得到的状态去进行转移&#xff0c;在q为100%的时候&#xff0c;我们能够知道赢的概率为 p,那么赢的期望就是…

java的学习,刷题

先来点题目看看 1031. 两个非重叠子数组的最大和 难度中等249收藏分享切换为英文接收动态反馈 给你一个整数数组 nums 和两个整数 firstLen 和 secondLen&#xff0c;请你找出并返回两个非重叠 子数组 中元素的最大和&#xff0c;长度分别为 firstLen 和 secondLen 。 长度…

【c语言】详解const常量修饰符 | 指针变量的不同const修饰

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

网络:DPDK复习相关知识点

1.转发模型&#xff1a; 1.1 运行至完成&#xff1a;run to complate &#xff08;RTC&#xff09; 参考笔记&#xff1a;DPDK介绍-CSDN博客 选择哪些核可以被DPDK使用&#xff0c;最后把处理对应收发队列的线程绑定到对应的核上&#xff0c;每个报文的生命周期都只能在其中一个…

电脑桌面日历怎么设置?超简单方法分享!

案例&#xff1a;电脑桌面日历怎么设置&#xff1f; 【最近因为工作的原因到了国外&#xff0c;但是电脑桌面的日历和时间一直都是错误的&#xff0c;急求一个设置电脑桌面日历的方法&#xff01;感谢大家&#xff01;】 电脑桌面日历是一种方便实用的工具&#xff0c;它可以…

liunx笔记

快捷键 #移动到行首 ctrla #移动到行尾 ctrle #删除光标之前的字符 ctrlu #删除光标之后的字符 ctrlk #清屏 ctrll正则表达式 正则中普通常用的元字符 元字符功能.匹配除了换行符以外的任意单个字符*前导字符出现0次或连续多次.*任意长度字符^行首(以…开头)&#xff0c;如…

浅谈浏览器调试

浅谈浏览器调试 1. 假设你在控制台打印了一个变量 &#xff0c; 想在浏览器追踪从哪里来要到那里去&#xff1a;2. 开启source maps3. watch 可以监听当前变量, 也可以在scope中看当前作用域的变量&#xff1a;4. call Stack 可以查看当前调用链, 找到对应代码进行debugger5. 你…

TCP / IP 五层网络模型

目录 一、协议的概念 1.1 网络通信为什么需要使用协议 二、网络模型 2.1 OSI 七层模型 三、TCP / IP 五层网络模型 四、协议分层的背景下&#xff0c;数据如何通过网络传输&#xff1f; 4.1 再谈协议 4.2 封装 / 分用 vs 序列化 / 反序列化 4.3 什么是端口号 4.4 什么…

创新实践|导致创新团队敏捷实践失败的12种原因

揭示敏捷实践中常犯的12大错误&#xff0c;了解如何避免这些敏捷失败 陷阱&#xff0c;找出问题根源并采取有效改进措施&#xff0c;提高项目成功率。立即连线 Runwise.co 社区敏捷专家获得专业建议&#xff0c;或 Runwise.co 在线学习敏捷方法实战课程&#xff0c;提升您和团队…

layui入门使用文档(包含几个重要的组件)

一、引言 &#xff1a;注意部分配置是用到了和jsp交互使用在HTML时需要修改 1.1 介绍 https://www.layuiweb.com/ 在官网首页&#xff0c;可以很方便的下载LayUI LayUI 是一款经典模块化前端 UI 框架&#xff0c;我们只需要定义简单的HTML、CSS、JS即可实现很复杂的前端效果。 …

Vue3之setup参数介绍

setup(props, context) {... }一、参数 使用setup函数时&#xff0c;它将接受两个参数&#xff1a; propscontext 让我们更深入地研究如何使用每个参数 二、Props setup函数中的第一个参数是props。正如在一个标准组件中所期望的那样&#xff0c;setup函数中的props是响应…