Redis从理论到实战:使用Redis实现商铺查询缓存(逐步分析缓存更新策略)

news2025/7/18 9:40:16

文章目录

  • 一、什么是缓存
  • 二、缓存的作用
  • 三、添加商户缓存
  • 四、分析缓存更新策略
    • 1、删除缓存还是更新缓存?
    • 2、如何保证缓存与数据库的操作同时成功或失败?
    • 3、先操作缓存还是先操作数据库?


加油加油,不要过度焦虑(#^.^#)

一、什么是缓存

缓存就是数据交换的缓冲区,是存贮数据的临时地方,一般读写性能较高
我们可以在很多地方做缓存,比如浏览器缓存、应用层缓存、数据库缓存等等
在这里插入图片描述


二、缓存的作用

  • 我们可以使用缓存,降低后端负载;
  • 使用缓存,可以提高读写效率,降低响应时间。

三、添加商户缓存

思路分析:

  • 首先从Redis中查询数据是否存在:如果存在,则返回数据;如果不存在,则访问数据库;
  • 接着从数据库中查询数据是否存在:如果存在,则从数据库中返回数据并写入redis缓存中;如果不存在,则提示用户不存在。

代码实现:

	//controller层
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
        return shopService.queryShopById(id);
    }
	//service层
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Result queryShopById(Long id) {
        String key = CACHE_SHOP_KEY + id;
        String shopCache = redisTemplate.opsForValue().get(key);
        //如果在缓存中查询到商户,则返回数据给前端
        if (StrUtil.isNotBlank(shopCache)) {
            Shop shop = JSONUtil.toBean(shopCache, Shop.class);
            System.out.println("shopCache" + shopCache);
            return Result.ok(shop);
        }
        //不存在则根据id在数据库中查找
        Shop shop = shopMapper.selectById(id);
        if (shop == null) {
            return Result.fail("店铺不存在");
        }
        //店铺存在,写入缓存
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));
        return Result.ok(shop);
    }

整体思路如下:

在这里插入图片描述


四、分析缓存更新策略

  • 刚才的代码在更新数据方面存在着一些问题,所以来一起讨论讨论redis的缓存更新策略。

Redis有三种缓存更新策略:

内存淘汰超时剔除主动更新
说明不需要自己维护,利用redis的内存淘汰机制,当内存不足时自动淘汰部分数据。下次查询时更新缓存给缓存数据添加ttl(过期)时间,到期后自动删除缓存。下次查询时更新缓存编写业务逻辑,在修改数据库的同时,更新缓存
一致性一般
维护成本

业务场景的使用:

业务场景低一致性需求高一致性需求
使用内存淘汰机制使用主动更新,并以超时剔除作为兜底方案

对于商户查询的缓存,这里使用的是主动更新策略,而对于主动更新策略的选择,又有三种方案,这里采用的是方案一:

方案一由缓存的调用者,在更新数据库的同时更新缓存
方案二缓存与数据库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题
方案三调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致

方案拿捏了,在操作缓存和数据库时,我们还需要考虑三个问题:

1、删除缓存还是更新缓存?

如果采用更新缓存,那么每次更新数据库时都会更新缓存,无效写操作较多,所以我们不采用;选择删除缓存,在更新数据库时让缓存失效,等到查询时再更新缓存。

2、如何保证缓存与数据库的操作同时成功或失败?

单体系统:将缓存与数据库操作放在一个事物
分布式系统:利用TCC等分布式事物方案

3、先操作缓存还是先操作数据库?

在这里插入图片描述

  • 如果先删除缓存再操作数据库,如图所示,在线程1删除缓存开始更新数据库时,线程2进来了,此时数据库还未更新,那么线程2写入的缓存就不是数据库更新后的数据
  • 如果先操作数据库再删除缓存,如图所示,在线程1查询数据库并写入缓存时,线程2进来了,此时线程2更新数据库并删除缓存,那么线程1写入的缓存也就不是数据库更新后的数据
  • 但是由于后者在写入缓存的时间低于数据库更新的时间,所以第二种选择更好,发生数据不同步的概率更低!

小总结:

  • 对于读操作:缓存命中则直接返回;缓存未命中则查询数据库,并写入缓存,设定超时时间。
  • 对于写操作:先写数据库,然后再删除缓存;要确保数据库与缓存操作的原子性。

代码实现:

  • 根据id查询店铺时,如果缓存未命中,则查询数据库,将数据库结果写入缓存,并设置超时时间。
  • 根据id修改店铺时,先修改数据库,再删除缓存。

service层代码:

	//查询商户
	@Override
    public Result queryShopById(Long id) {
        String key = CACHE_SHOP_KEY + id;
        String shopCache = redisTemplate.opsForValue().get(key);
        //如果在缓存中查询到商户,则返回数据给前端
        if (StrUtil.isNotBlank(shopCache)) {
            Shop shop = JSONUtil.toBean(shopCache, Shop.class);
            return Result.ok(shop);
        }
        //不存在则根据id在数据库中查找
        Shop shop = shopMapper.selectById(id);
        if (shop == null) {
            return Result.fail("店铺不存在");
        }
        //店铺存在,写入缓存,过期时间设置为30分钟
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return Result.ok(shop);
    }
    //更新商户
    @Override
    @Transactional
    public Result updateShop(Shop shop) {
        Long shopId = shop.getId();
        if (shopId == null) {
            return Result.fail("店铺id不能为空");
        }
        //先更新数据库
        shopMapper.updateById(shop);
        //再删除缓存
        redisTemplate.delete(CACHE_SHOP_KEY + shopId);
        return Result.ok();
    }

controller层代码:

    /**
     * 根据id查询商铺信息
     * @param id 商铺id
     * @return 商铺详情数据
     */
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
        return shopService.queryShopById(id);
    }
    /**
     * 更新商铺信息
     * @param shop 商铺数据
     * @return 无
     */
    @PutMapping
    public Result updateShop(@RequestBody Shop shop) {
        return shopService.updateShop(shop);
    }

到此结束,有什么疑问可以一起讨论~

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

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

相关文章

ThreadLocal为什么会出现内存泄漏,你真的知道吗?

目录 1 前言 2 ThreadLocal进行线程隔离的小示例 3 原因 1 前言 大家想要搞清楚这个问题,就必须知道内存泄漏和内存溢出的区别 内存泄漏:不就被使用的对象或者变量无法被回收 内存溢出:没有剩余的空间来创建新的对象 2 ThreadLocal进行…

Java中的字符串

🙉 作者简介: 全栈领域新星创作者 ;天天被业务折腾得死去活来的同时依然保有对各项技术热忱的追求,把分享变成一种习惯,再小的帆也能远航。 🏡 个人主页:xiezhr的个人主页 java中的字符串一、简…

C++:重定义:符号重定义:变量重定义

概述:在上一篇我们知道 通过 #ifndef....#defin....#endif , 这个解决头文件重复包含的问题 C:重定义:class类型重定义_hongwen_yul的博客-CSDN博客 避免头文件的重复包含可以有效的避免变量的重复定义,其实不光变量…

[附源码]java毕业设计基于web旅游网站的设计与实现

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

使用Docker开发GO应用程序

根据Stack Overflow的2022开发者调查,Go(或Golang)是最受欢迎和最受欢迎的编程语言之一。由于与许多其他语言相比,Go的二进制大小更小,开发人员经常使用Go进行容器化应用程序开发。 Mohammad Quanit在他的社区全能课程…

小程序vant-tabbar使用示例,及报错处理

小程序vant-tabbar使用示例,及报错处理1. 配置信息2. 添加 tabBar 代码文件3. 编写 tabBar 代码custom-tab-bar/index.tscustom-tab-bar/index.jsoncustom-tab-bar/index.wxml使小程序使用vant-tabbar组件时,遇到以下报错:Couldn’t found th…

Java基于springboot+vue的儿童玩具销售购物网站 多商家

爱玩儿是所有孩子的天性。尤其是在婴幼儿阶段。选择一个好的玩具,不仅能够让孩子玩儿的开心,而且有助于孩子智力的开发。很多家长在选择玩具的时候,不知道选择什么样的玩具。且当前玩具市场的玩具鱼目混杂,种类繁多,而…

SAR信号处理基础1——线性调频信号

关键字:线性调频信号,LFM信号,chirp信号,驻定相位原理(POSP),泰勒展开,Taylor展开,脉冲压缩,匹配滤波,sinc,分辨率,峰值旁…

QProgressDialog.close()失败,进度条关闭感觉失败了,无法彻底关闭

开发环境:我是在deepin(深度)系统下开发的,在我本机上,一点问题也没有,但是我移植到了ubantu的机子上,就偶尔出现出个问题,出现了一个模态框,需要重启软件才能关闭。 问题…

Vue的computed和watch的区别是什么?

一、computed介绍 computed 用来监控自己定义的变量&#xff0c;该变量在 data 内没有声明&#xff0c;直接在 computed 里面定义&#xff0c;页面上可直接使用。 //基础使用 {{msg}} <input v-model"name" /> //计算属性 computed:{msg:function(){return …

【MySQL】MySQL日志系统以及InnoDB背后的技术(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

Java基于springboot+vue的个人博客网站 前后端分离

随着现在网络的快速发展&#xff0c;网上管理系统也逐渐快速发展起来&#xff0c;网上管理模式很快融入到了许多网站的之中&#xff0c;随之就产生了“博客网站”&#xff0c;这样就让博客网站更加方便简单。 对于本博客网站的设计来说&#xff0c;系统开发主要是采用java语言技…

2022国产8K摄像机介绍

摄像机是一种把光学图像信号转变为电信号&#xff0c;以便于存储或者传输的设备。当我们拍摄一个物体时&#xff0c;此物体上反射的光被摄像机镜头收集&#xff0c;使其聚焦在摄像器件的受光面&#xff08;例如摄像管的靶面&#xff09;上&#xff0c;再通过摄像器件把光转变为…

N-HiTS: Neural Hierarchical Interpolation for Time Series Forecasting

N-HiTS: Neural Hierarchical Interpolation for Time Series Forecasting 神经预测的最新进展加速了大规模预测系统性能的提高。然而,长期预测仍然是一项非常困难的任务。影响这项任务的两个常见挑战是预测的波动性和它们的计算复杂性。本文提出N-HiTS,一种通过结合新的分层…

不同字符编码对比

目录 1. ASCII码 2. Unicode 3. GBK编码 1. ASCII码 ASCII码使用一个字节编码&#xff0c;但只适用于英文&#xff1b; 2. Unicode Unicode定义了字符集&#xff0c;有 17 个 code plane&#xff0c;总共规划了 1,114,112 个 code point。而这些字符可以使用UTF-8、UTF-1…

Windows无法访问指定设备、路径或文件怎么办?

如何解决Windows 无法访问指定的设备、路径或文件错误&#xff1f; 1.修改安全中心的设置 如果在安装程序的过程中&#xff0c;遇到该错误&#xff0c;可以进入到【Windows安全中心】进行设置修改。 第一步&#xff1a;点击左下角的开始按钮&#xff0c;然后依次点击【设置】…

直接安装WSL2及安装Ubuntu到F盘

1. 勾选这三项&#xff0c;重启 2. 以管理员方式运行powersell wsl --updatewsl --shutdownwsl --set-default-version 2wsl --status3. 解压缩ubuntu 解压缩Ubuntu_1804.2019.522.0_x64.appx到F盘 4. 安装ubuntu 双击ubuntu1804.exe安装 5. 运行 双击ubuntu1804.exe …

AI人工智能实践技术系统性教学方案

郁磊老师【副教授】 &#xff1a;长期从事Python、Matlab机器学习及深度学习等研究工作&#xff0c;具备良好的数学及信号处理基础&#xff0c;熟悉如神经网络、支持向量机、决策树、随机森林等&#xff0c;以及群优 化算法&#xff0c;如遗传算法、蚁群算法、蝙蝠算法等&#…

React源码解读之更新的创建

React 的鲜活生命起源于 ReactDOM.render &#xff0c;这个过程会为它的一生储备好很多必需品&#xff0c;我们顺着这个线索&#xff0c;一探婴儿般 React 应用诞生之初的悦然。 更新创建的操作我们总结为以下两种场景 ReactDOM.rendersetStateforceUpdate ReactDom.render …

k8s-dynamic-pvc

安装 storage class: external-storage/nfs-client/deploy at master kubernetes-retired/external-storage GitHub 下载文件并安装: class.yaml deployment.yaml rbac.yaml 其中修改: 安装 测试: [rootmaster test-dir]# cat nginx-1.yaml apiVersion: v1 kind: Pers…