【SpringBoot篇】解决缓存击穿问题② — 基于逻辑过期方式

news2025/2/1 21:47:51

🎊专栏【SpringBoot】
🍔喜欢的诗句:天行健,君子以自强不息。
🎆音乐分享【如愿】
🎄欢迎并且感谢大家指出小吉的问题🥰

文章目录

  • 🎍什么是逻辑过期方式
  • ⭐思路
  • 🌹代码

在这里插入图片描述

🎍什么是逻辑过期方式

逻辑过期是一种指定缓存数据失效时间的方式,与物理过期不同。逻辑过期并不直接将缓存中的数据删除,而是在缓存中保留该数据,但标记其为过期,表示该数据已经不再可用。

在逻辑过期的情况下,当有请求查询该数据时,缓存会先检查该数据是否过期,如果过期,则缓存会认为该数据不存在,并重新从数据源获取最新的数据。如果数据没有过期,则直接返回缓存中的数据。需要注意的是,逻辑过期时间是相对较短的,通常设置在几分钟或者几十分钟之内。

与物理过期相比,逻辑过期具有以下优点:

  • 提高了缓存的利用率:逻辑过期可以在数据失效后仍然保留数据,提高了缓存的利用率,减少了对数据源的访问次数。
  • 减少了缓存穿透的问题:即使缓存中不存在某个数据,逻辑过期也可以在一定时间内避免大量的访问请求落到数据源上,从而减轻了数据源的负担。
  • 提高了系统的性能:逻辑过期可以缩短缓存数据的更新频率,从而提高了系统的响应速度和性能。

总之,逻辑过期是一种有效的缓存策略,能够提高系统的性能和可用性。需要根据具体业务场景和数据特点选择合适的逻辑过期时间,以达到最优的缓存效果。

⭐思路

基于逻辑过期的方式解决缓存穿透问题的思路是通过在缓存中设置较短的逻辑过期时间来处理查询不存在的数据。这种方式的核心理论是将缓存和数据源之间的查询请求进行分流,减轻数据源的负担,并提高系统的响应速度。

具体来说,当一个请求到达时,先检查缓存中是否存在所需数据。如果缓存中不存在该数据,则说明可能发生了缓存穿透。为了避免直接向数据源发起查询请求,并且继续保持对数据的查询,我们通过设置逻辑过期时间来抑制该请求。也就是说,将该请求的结果设置为空,并设置一个较短的逻辑过期时间。

这样一来,在逻辑过期时间内,其他同样请求该数据的请求会继续从缓存中获取旧的空结果。这样可以避免大量请求直接访问数据源,减轻了数据源的压力。同时,在逻辑过期时间到期后,新的请求会再次触发查询数据源的操作,以更新缓存中的数据。这样可以保证缓存中的数据与数据源的一致性。

从理论上讲,基于逻辑过期的方式能够有效地处理缓存穿透问题。通过将不存在的数据也缓存起来,并设置较短的逻辑过期时间,可以在一段时间内屏蔽掉大量的查询请求,减轻了数据源的负担。而在逻辑过期时间到期后,通过更新缓存的方式保证了数据的一致性,使得后续的请求可以从缓存中获取到最新的数据。

需要注意的是,选择适当的逻辑过期时间非常重要。过长的逻辑过期时间可能导致缓存数据与实际数据不一致,而过短的逻辑过期时间则可能增加了缓存的更新频率,影响系统的性能。在实际应用中,需要根据具体业务场景和数据特点进行调整,找到一个合适的平衡点。

🌹代码

请添加图片描述

我们把数据写入Redis里面的时候,我们要设置一个逻辑过期时间

在这里插入图片描述

我们把店铺数据加入到缓存当中

在这里插入图片描述

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryById(Long id) {

        //逻辑过期解决缓存击穿
        Shop shop=queryWithLogicalExpire(id);
        if(shop==null){
            return Result.fail("店铺不存在");
        }

        //返回
        return Result.ok(shop);
    }

    //创建一个线程池
    private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);

    public Shop queryWithLogicalExpire(Long id) {
        String key = CACHE_SHOP_KEY + ":" + id;
        //从redis中查询缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //判断是否存在
        if (StrUtil.isBlank(shopJson)) {
            //存在,直接返回
            return null;
        }
        //命中
            //需要先把json反序列化为对象
        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
        JSONObject data = (JSONObject) redisData.getData();
        Shop shop = JSONUtil.toBean(data, Shop.class);
        LocalDateTime expireTime = redisData.getExpireTime();

        //判断缓存是否过期
        if (expireTime.isAfter(LocalDateTime.now())) {
            //未过期,直接返回店铺信息
            return shop;
        }
        //过期,需要缓存重建
            //缓存重建
            //获取互斥锁
        String lockKey = "lock:shop" + id;
        boolean isLock = tryLock(lockKey);
        //判断是否获取锁成功
        if (isLock) {
            //成功,开启独立线程,实现缓存重建
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                try {
                    //缓存重建
                    this.saveShop2Redis(id, 30L);
                }catch (Exception e) {
                    throw new RuntimeException(e);
                }finally {
                    //释放锁
                    unlock(lockKey);
                }
            });
        }
        //返回
        return shop;
    }
    //获取锁
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

    //释放锁
    private void unlock(String key){
        stringRedisTemplate.delete(key);
    }

    public void saveShop2Redis(Long id,Long expireSeconds){
        //查询店铺数据
        Shop shop=getById(id);

        //封装逻辑过期时间
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusMinutes(expireSeconds));

        //写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+":"+id, JSONUtil.toJsonStr(redisData));
    }

}

在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!

在这里插入图片描述

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

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

相关文章

Spring实战系列(三)了解容器的基本实现

我们可以通过GitHub或者码云下载spring-framework源码&#xff0c;这边是基于5.X版本进行下载学习的。 地址&#xff1a;https://github.com/spring-projects/spring-framework 分析Spring源码是非常一件的难的事情&#xff0c;只能一步步学习&#xff0c;一步步记录。 前面在…

人工智能的弱点有哪些?

尽管人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;在许多领域取得了巨大的进展和成就&#xff0c;但它仍然存在一些弱点和挑战。以下是人工智能的一些常见弱点&#xff1a; 1. 数据依赖性&#xff1a;人工智能算法通常需要大量的高质量数据进行训练…

每日一题(LeetCode)----二叉树-- 二叉树的右视图

每日一题(LeetCode)----二叉树-- 二叉树的右视图 1.题目&#xff08;199. 二叉树的右视图&#xff09; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,nu…

flex--伸缩性

1.flex-basis flex-basis 设置的是主轴方向的基准长度&#xff0c;会让宽度或高度失效。 备注&#xff1a;主轴横向&#xff1a;宽度失效&#xff1b;主轴纵向&#xff1a;高度失效 作用&#xff1a;浏览器根据这个属性设置的值&#xff0c;计算主轴上是否有多余空间&#x…

微信小程序picker组件扩展选择时间到秒插件

创建插件seldatetime // 插件JS部分 Component({// 一些选项options: {// 样式隔离&#xff1a;apply-shared 父影响子&#xff0c;shared父子相互影响&#xff0c; isolated相互隔离styleIsolation:"isolated",// 允许多个插槽multipleSlots: true},// 组件的对外属…

k8s的二进制部署(一)

k8s的二进制部署&#xff1a;源码包部署 环境&#xff1a; k8smaster01: 20.0.0.71 kube-apiserver kube-controller-manager kube-schedule ETCD k8smaster02: 20.0.0.72 kube-apiserver kube-controller-manager kube-schedule Node节点01: 20.0.0.73 kubelet kube-pr…

2008年AMC8数学竞赛中英文真题典型考题、考点分析和答案解析

今天我们来看看2008年AMC8竞赛的五道典型考题。欢迎您查看历史文章了解之前各年的真题解析&#xff0c;本系列会持续更新&#xff0c;直到大家参加完2024年的比赛。您有任何关于AMC8比赛的任何问题都可以问我&#xff0c;关于题目的解析也可以交流。 【推荐】为帮助孩子们更便…

人工智能_机器学习076_Kmeans聚类算法_体验_亚洲国家队自动划分类别---人工智能工作笔记0116

我们开始来看聚类算法 可以看到,聚类算法,其实就是发现事物之间的,潜在的关联,把 有关联的数据分为一类 我们先启动jupyter notebook,然后 我们看到这里我们需要两个测试文件 AsiaFootball.txt里面记录了,3年的,亚洲足球队的成绩

C语言转WebAssembly的全流程,及测试

第一步&#xff1a;安装环境 参考网址&#xff1a;https://emscripten.org/docs/getting_started/downloads.html 具体过程&#xff1a; 克隆代码&#xff1a;git clone https://github.com/emscripten-core/emsdk.git进入代码目录&#xff1a;cd emsdk获取最新远端代码&…

阿赵UE学习笔记——5、创建关卡元素

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   之前介绍了从空白模板创建关卡&#xff0c;接下来尝试着在这个空白的世界里面&#xff0c;创建一些内容。 一、创建地面 1、创建面片作为地面 创建——形状——平面&#xff0c;可以创建一个面片 在细节面板设置合适的…

深入了解云原生:定义与特征解析

文章目录 一、云原生概述1.1 什么是云原生1.2 云原生组成要素1.3 补充资料 二、云原生的目标2.1 云原生关键目标2.2 云原生特性 三、云原生应用 VS 传统单体应用参考资料 一、云原生概述 1.1 什么是云原生 (1)云原生定义 云原生(Cloud Native) 是一种软件架构和开发方法论&a…

云计算IaaS、PaaS和SaaS之

提供的服务来比较如下两图 示例图 示例图

PYTHON基础:决策树与随机森林算法

决策树与随机森林算法 决策树和随机森林都是用于分类和回归的的算法。决策树的原理是通过一系列的问题进行if、else的推导。随机森林是集合学习算法&#xff0c;即把很多的机器学习算法综合在一起组成一个更大的模型。 决策树的优劣势&#xff1a;处理容易&#xff0c;不需要…

DS八大排序之归并排序和计数排序

前言 前几期我们详细介绍了插入排序&#xff08;直接插入排序和希尔排序&#xff09;、选择排序&#xff08;直接选择和堆排序&#xff09;、交换排序&#xff08;冒泡排序和快速排序&#xff09;。并对快排的各个版本做了详细的介绍&#xff0c;本期我们来介绍把最后两个即外…

关于“Python”的核心知识点整理大全41

目录 scoreboard.py game_functions.py game_functions.py 14.3.8 显示等级 game_stats.py scoreboard.py scoreboard.py scoreboard.py game_functions.py game_functions.py alien_invasion.py 14.3.9 显示余下的飞船数 ship.py scoreboard.py 我们将最高得分圆整…

大数据与人工智能|全面数字化战略与企业数字化转型(第1节 )

要点一&#xff1a;培养跨学科思维 在分析时&#xff0c;需要采用多学科的思维方式 结果不重要&#xff0c;重要的是如何提炼现象、分析问题和得出结论的过程。 1. 介绍了锤子精神和多学科思维方式的重要性。指出了只从自身学科出发解决问题的局限性。 2. 提倡跨学科思维方式&a…

家校互通小程序实战开发02首页搭建

目录 1 创建应用2 搭建首页总结 我们上一篇介绍了家校互通小程序的需求&#xff0c;创建了对应的数据源。有了这个基础的分析之后&#xff0c;我们就可以进入到开发阶段了。开发小程序&#xff0c;先需要创建应用。 1 创建应用 登录控制台&#xff0c;点击创建应用&#xff0c…

2024年深度学习、计算机视觉与大模型面试题综述,六大专题数百道题目

DeepLearning-Interview-Awesome-2024 本项目涵盖了大模型(LLMs)专题、计算机视觉与感知算法专题、深度学习基础与框架专题、自动驾驶、智慧医疗等行业垂域专题、手撕项目代码专题、优异开源资源推荐专题共计6大专题模块。我们将持续整理汇总最新的面试题并详细解析这些题目&a…

元宇宙与VR虚拟现实的未来如何?

从科幻小说到商业现实 自从 Facebook年更名为 Meta 以来&#xff0c;关于元宇宙的热议不断&#xff0c;人们对虚拟世界的兴趣也重新燃起&#xff0c;因为尽管虚拟现实 (VR) 的概念由来已久&#xff0c;但该技术现在才开始真正得以应用。 定义元宇宙和虚拟现实 首先是 The Met…

玩客云 青龙面板

一、刷机 需要的工具&#xff0c;镊子&#xff0c;双公头USB&#xff08;可以自己做&#xff09;&#xff0c;U盘 青龙面板全教程 | Anubis的小窝 powersee教程 玩客云导航固件使用说明 安装教程 玩客云乱七八糟的坑 静态IP配置 玩客云第二版固件说明 docker 下载器 …