数据缓存(Redis, Spring Cache)——后端

news2024/11/25 4:22:19

场景:给用户端展示的数据都是通过查询数据库所得,因此数据库访问压力会随着用户访问量增大而增加,从而导致系统响应慢、用户体验差。
方法:通过Redis缓存数据,减少查询数据库操作。(Redis的数据是存储在内存的,数据库的数据存储在磁盘,访问内存会更快。Redis相关知识可参见Redis基础)
在这里插入图片描述
举例:
以外卖平台为例,页面根据分类来展示菜品。当选择一个分类时,页面展示该分类的所有菜品,因此,缓存逻辑为:

  • 每个分类下的菜品对应一份缓存数据;
  • 数据库中菜品数据有变更时,清理缓存数据。(避免数据不一致)

综上,Redis的数据结构为:
在这里插入图片描述
(ps:Redis中的数据类型和Java中的数据类型并不是完全对应的,Java中的任何一个对象都可以转成Redis中的string字符串进行存储。若是从Java层面考虑,这里的value实则是List集合,然后把这个集合序列化,最终把它转成Redis字符串存储。dish_id表示分类id。)

代码举例:
使用Redis缓存访问数据:——用户端实现

@RestController("userDishController")
@RequestMapping("/user/dish")
@Api(tags = "C端-菜品相关接口")
public class DishController {

    @Autowired
    private DishService dishService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 条件查询菜品和口味
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("条件查询菜品和口味")
    public Result<List<DishVO>> listWithCategoryId(Long categoryId) {

        // 1. 构造redis中的key, 规则:dish_分类id
        String key = "dish_" + categoryId;

        // 2. 查询Redis是否存在菜品数据
        List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);

        // 3. 如果存在,直接返回,无须查询数据库
        if (list != null && list.size() > 0) {
            return Result.success(list);
        }

        // 4. 如果不存在,查询数据库,将查询到的数据放入Redis中
        Dish dish = new Dish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE); // 查询起售中的菜品

        list = dishService.listWithFlavor(dish);
        redisTemplate.opsForValue().set(key,list);// 存储查询结果到list
        return Result.success(list);
    }
}

清除缓存数据——管理端实现
需要清除缓存数据的情况:

  • 修改了数据库中的数据;
  • 删除了数据库中的数据;
  • 新增了数据库中的数据;
    总结:只要涉及到了对应数据库的增删改,都需要清除缓存。

代码中涉及的不同情况:

  • case1: 新增一个菜品——删除该菜品所属分类对应的缓存;
  • case2: 批量删除菜品——删除所有分类对应的缓存(删除时是根据菜品的id删除的,所以想要知道菜品所对应的分类id,还遍历查询一遍);
  • case3: 修改菜品(涉及两种情况:a. 只修改菜品的基本信息,菜品的分类不变;b. 修改了菜品的分类,这就涉及到了两个分类对应的缓存值)——删除所有分类对应的缓存(与上面的情况一样,如果要确定哪两个分类受影响,还得查一次(查修改前所属分类id),索性直接删除所有的分类的缓存)
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {
    @Autowired
    private DishService dishService;

    @Autowired
    private RedisTemplate redisTemplate;
    
    /**
     * 清理缓存数据
     * @param pattern
     */
    private void cleanCache(String pattern){
        Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);
    }

    /**
     * 新增菜品(case1)
     *
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO) {
        log.info("新增菜品:{}", dishDTO);
        dishService.saveWithFlavor(dishDTO);

        // 清理缓存数据
        String key = "dish_" + dishDTO.getCategoryId();
        cleanCache(key);
        
        return Result.success();
    }

    /**
     * 菜品批量删除(case2)
     *
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("菜品删除")
    // 通过@RequestParam解析前端请求地址中字符串参数ids=1,2,3...为一个列表
    public Result delete(@RequestParam List<Long> ids) {
        log.info("菜品批量删除:{}", ids);
        dishService.deleteBatch(ids);

        // 清理缓存数据(将所有的dish_*的缓存数据都清除掉)
        cleanCache("dish_*");
        
        return Result.success();
    }

    /**
     * 修改菜品(case3)
     *
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO) {
        log.info("修改菜品:{}", dishDTO);
        dishService.updateWithFlavor(dishDTO);
        
        // 清理缓存数据(将所有的dish_*的缓存数据都清除掉)
        cleanCache("dish_*");
        return Result.success();
    }

Spring Cache
Spring Cache是Spring框架提供的缓存工具,使用它可以进一步简化代码。它实现了基于注解的缓存功能,我们只需要添加一个注解,就能实现缓存功能。
Spring Cache只是提供了一层抽象,底层可以切换不同的缓存实现,包括EHCache,Caffeine,Redis等。我们只需要导入对应的缓存产品的客户端,就可以告诉Spring Cache我们想用哪个缓存实现。
maven坐标:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

Spring Cache提供的常用注解:

注解说明
@EnableCaching开启缓存注解功能,通常加在启动类上
@Cacheable在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中
@CachePut将方法的返回值放到缓存中
@CacheEvict将一条或多条数据从缓存中删除

使用Spring Cache缓存数据,@CachePut中key的生成:cacheNames::key,这里使用SqEL语法,SqEL细则:

Spring Expression Language (SpEL) expression for computing the key dynamically.
Default is “”, meaning all method parameters are considered as a key, unless a custom keyGenerator has been set.
The SpEL expression evaluates against a dedicated context that provides the following meta-data:

  • #result for a reference to the result of the method invocation. For supported wrappers such as Optional, #result refers to the actual object, not the wrapper
  • #root.method, #root.target, and #root.caches for references to the method, target object, and affected cache(s) respectively.
  • Shortcuts for the method name (#root.methodName) and target class (#root.targetClass) are also available.
  • Method arguments can be accessed by index. For instance the second argument can be accessed via #root.args[1], #p1 or #a1. Arguments can also be accessed by name if that information is available.

举例:

 @CachePut(cacheNames = "setmeal",key = "#setmealDTO.categoryId")

在这里插入图片描述

注意: 不同的注解对应支持生成key的方式可能不一样,比如@Chacheable中就不支持#result的形式,所以使用的时候可以通过 Ctrl +鼠标点击key跳转到源文件中去查看。

举例:
实现思路如下:

  • 导入Spring Cache和Redis相关maven坐标
  • 在启动类上加入@EnableCaching注解,开启缓存注解功能
  • 在用户端接口查询 方法上加入@Cacheable注解
  • 在管理端接口数据库增、删改等方法上加入CacheEvict注解

@CacheAble举例:

    /**
     * 条件查询
     * @param categoryId
     * @return
     */
    @GetMapping("list")
    @ApiOperation("根据分类id查询套餐")
    // key为: setmealCache::categoryId值, Redis存储的value为list方法的返回结果
    @Cacheable(cacheNames = "setmealCache", key = "#categoryId") 
    public Result<List<Setmeal>> list(Long categoryId){
        Setmeal setmeal = new Setmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);
        List<Setmeal> etmealVOList = setmealService.list(setmeal);
        return Result.success(etmealVOList);
    }

@CacheEvic举例:
精确清理:

    /**
     * 新增套餐
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    @CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
    public Result save(@RequestBody SetmealDTO setmealDTO){
        log.info("新增套餐:{}",setmealDTO);
        setmealService.save(setmealDTO);
        return Result.success();
    }

全部清理:

    /**
     * 批量删除套餐
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("批量删除套餐")
    @CacheEvict(cacheNames = "setmealCache",allEntries = true)
    public Result deleteBatch(Long[] ids){
        setmealService.deleteBatch(ids);
        return Result.success();
    }

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

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

相关文章

vscode 支持c,c++编译调试方法

概述&#xff1a;tasks.jason launch.json settings.json一定要有&#xff0c;没有就别想跑。还有就是c 和c配置有区别&#xff0c;切记&#xff0c;下文有说 1.安装扩展插件。 2.安装编译器&#xff0c;gcc.我用的是x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z &#xf…

GLTF编辑器-位移贴图实现破碎的路面

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 位移贴图是一种可以用于增加模型细节和形状的贴图。它能够在渲染时针…

水准网、平面导线平差

东北大学测绘工程水准网、平面闭合导线间接平差法平差C#项目。 闭合导线程序界面&#xff1a; 水准网程序界面&#xff1a; 项目gitee地址&#xff1a; horizon: 东北大学测绘工程水准网&#xff0c;闭合导线间接平差法C#项目 (gitee.com) 注&#xff1a;此项目为本博主代人转…

DCDC--电感的选择和影响

1、感值L的影响 1.1、纹波Ripple的影响&#xff1a;感值越大&#xff0c;纹波越小 1.2、负载瞬态响应Load Transient的影响&#xff1a;感值越大&#xff0c;负载瞬态响应越差 2、直流电阻DCR的影响 2.1、效率Efficiency的影响 相同型号&#xff0c;感值越大&#xff0c;DC…

笔记1:基于锚框(先验框)的目标检测

一、边缘框&#xff08;bounding box&#xff09; 1.1 定义 边缘框&#xff1a;真实标注的物体位置 2.1 表示方式 1、&#xff08;x1,y1)和(x2,y2) 2、&#xff08;x1,y1)和w,h 二、锚框(anchor box)/先验框&#xff08;prior bounding box&#xff09; 2.1 定义 对边缘…

vue 拖拽通过子元素拖拽父元素指令

vue 拖拽通过子元素拖拽父元素指令 需求 拖拽头部 拖动整个框 // candrag.js中的代码如下 directive // 通过子元素 控制移动父元素&#xff0c; 如果 需要直接控制父元素可以再写一个自定义指令 或者改造下这个指令 export default {// 定义 Vue 插件install(Vue) {Vue.di…

抬头举手阅读YOLOV8NANO

首先用YOLOV8NANO得到PT模型&#xff0c;转换成ONNX,OPENCV调用&#xff0c;PYTHON,C,ANDROID都可以举手写字阅读YOLOV8NANO

QT上位机开发(开篇)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 可能是因为03年上大学的原因&#xff0c;那个时候学习的编译工具主要就是VC6&#xff0c;一个普遍被认为是古老的开发工具。如果要编写界面的话&am…

java设计模式学习之【访问者模式】

文章目录 引言访问者模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用电脑示例代码地址 引言 设想你是一个艺术馆的管理员&#xff0c;艺术馆里有各种各样的艺术品。每当有游客来访时&#xff0c;根据他们的兴趣&#xff0c;他们可能只想看画、雕塑或特定的…

私有部署ELK,搭建自己的日志中心(六)-- 引入kafka对采集日志进行削峰填谷

一、背景 首先&#xff0c;要说明一点&#xff0c;elk日志中心&#xff0c;是可以缺少kafka组件的。 其次&#xff0c;如果是研发环境下&#xff0c;机器资源紧张的情况下&#xff0c;也是可不部署kafka。 最后&#xff0c;因为kafka的部署是可以独立的&#xff0c;所以本文将…

成都服装(服饰)行业2023年度工作总结表彰大会暨五圣荟户外大本营签约发布会

凝心聚力谋发展&#xff0c;稳中求进谱新篇&#xff0c;由成都服装&#xff08;服饰&#xff09;行业协会主办&#xff0c;成都梵圣万汇文化发展有限公司承办的成都服装&#xff08;服饰&#xff09;行业2023年度工作总结表彰大会暨五圣荟户外大本营签约发布会于2023年12月28日…

数据库系统(六)数据库范式 | 函数依赖,一二三范式,BCNF,属性集闭包和正则覆盖

文章目录 1 好的关系设计的特征2 函数依赖关系3 Normal Forms 规范形式3.1 一二三范式3.1.1 基本概念3.1.2 判断是否满足3NF 3.2 BCNF3.2.1 基本概念3.2.2 判断是否满足BCNF3.2.3 分解得到BCNF 4 属性集闭包和正则覆盖4.1 属性集闭包求法4.2 属性集闭包应用4.2.1 测试某个属性集…

借助 Google Play 游戏电脑版新功能,加速业务增长

作者 / Google Play 游戏总监 Arjun Dayal Google Play 游戏电脑版测试版自去年发布以来&#xff0c;取得了巨大的发展。Google Play 游戏电脑版现在提供 3,000 多种游戏&#xff0c;覆盖 120 多个国家/地区的用户&#xff0c;为玩家提供各种类型的游戏。我们的热门移动游戏目录…

4-链表-合并两个有序链表

这是链表的第4题&#xff0c;来个简单算法玩玩。力扣链接。 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff…

Kindle使用USB数据线传书封面无法显示问题

以下内容只针对USB传书&#xff08;非越狱版本&#xff0c;越狱了有相关插件&#xff0c;这里不谈&#xff09;&#xff0c;不包括邮件传书。 恶心图如下&#xff1a; 直接把mobi/azw3/azw &#xff08;epub模式不能直接拷贝&#xff0c;kindle无法读取&#xff09;格式的电子…

99. 恢复二叉搜索树

#中序遍历&#xff0c;寻找插值位置并交换 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def recoverTree…

基于MINIST的手写数字体识别

一、算法简述 网络结构设计 通过创建MnistNet类&#xff0c;定义了包含两个卷积层和两个全连接层的深度神经网络。这个网络的设计灵感来自于经典的CNN结构&#xff0c;其中卷积层用于提取图像特征&#xff0c;而全连接层则用于将这些特征映射到最终的类别。 卷积与池化 卷…

新能源汽车高性价比电驱系统设计技术

版权声明&#xff1a;此资源由用户收集整理于网络&#xff0c;只用于交流学习&#xff0c;请勿用作它途。除非确实无法确认&#xff0c;我们都会注明作者和来源&#xff0c;无法确认情况我们标注来自网络。若涉及版权问题&#xff0c;烦请原作者联系我们&#xff0c;与您共同协…

【软件工程】漫谈增量过程模型:软件开发的逐步之道

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 软件工程 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言&#xff1a; 正文 增量过程模型&#xff08;Incremental Process Model&#xff09; 主要特点和阶段&#xff1a; 优点&#xff1…

第五课:集成电路与摩尔定律(硬件的发展)、操作系统、内存和储存介质(存储技术的发展)、文件系统、压缩、命令行界面及屏幕与 2D 图形显示

第五课&#xff1a;集成电路与摩尔定律&#xff08;硬件的发展&#xff09;、操作系统、内存和储存介质&#xff08;存储技术的发展&#xff09;、文件系统、压缩、命令行界面及屏幕与 2D 图形显示 第十七章&#xff1a;集成电路与摩尔定律&#xff08;硬件的发展&#xff09;1…