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

场景:给用户端展示的数据都是通过查询数据库所得,因此数据库访问压力会随着用户访问量增大而增加,从而导致系统响应慢、用户体验差。
方法:通过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 {@Autowiredprivate DishService dishService;@Autowiredprivate RedisTemplate redisTemplate;/*** 条件查询菜品和口味** @param categoryId* @return*/@GetMapping("/list")@ApiOperation("条件查询菜品和口味")public Result<List<DishVO>> listWithCategoryId(Long categoryId) {// 1. 构造redis中的key, 规则:dish_分类idString 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);// 存储查询结果到listreturn Result.success(list);}
}

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

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

代码中涉及的不同情况:

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

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

相关文章

Qt 5.9.4 转 Qt 6.6.1 遇到的问题总结(一)

最近公司对大家的开发的硬件环境进行了升级&#xff0c;电脑主机的配置、显示器&#xff08;两台大屏显示器&#xff09;变得的逼格高多了。既然电脑上的开发环境都需要重装&#xff0c;就打算把开发环境也升级到最新版本&#xff0c;要用就用最新版本。下面对升级后的开发环境…

ramdump 中的memory统计

0. 前言 ramdump是指某个时刻系统或者子系统发生crash等异常&#xff0c;系统将内存中的数据通过一定的方式保存下来&#xff0c;相当于一个系统内存快照&#xff0c;用以开发者离线分析系统异常问题。 ramdump 工具中有很多内存统计的脚本&#xff0c;本文逐一剖析内存相关的…

需求分析 :不得不重新去面对的一关。

软件需求分析 背景 深入需求产生的背景明确项目目标了解用户群体 需求优先级 需求的分类与整理明确需求优先级让团队成员都参与到需求分析中来&#xff0c;增加团队合作能力与效率 编写需求文档 整理好的需求编写成详细的需求文档包括需求的描述、输入/输出格式、功能流程…

Leetcode算法系列| 10. 正则表达式匹配

目录 1.题目2.题解C# 解法一&#xff1a;分段匹配法C# 解法二&#xff1a;回溯法C# 解法三&#xff1a;动态规划 1.题目 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 1.‘.’ 匹配任意单个字符 2.‘.’ 匹配任意单个字…

网络运行状况监控工具

网络运行状况是网络在其操作和环境约束范围内按预期运行的能力&#xff0c;但是&#xff0c;随着云和人工智能等技术的出现&#xff0c;网络变得越来越复杂&#xff0c;维护其 IT 基础设施是一项越来越繁琐的任务。为了确保网络可靠性&#xff0c;组织需要了解每个端点的运行状…

使用Ubuntu编译FFmpeg生成Android动态库/静态库

环境 我这里使用windows里的wsl2的ubuntu&#xff0c;使用物理机或者vmware&#xff0c;vbox之类的安装的ubuntu理论上也可以. gcc编译使用的环境如下: Ndk使用17 FFmpeg使用4.0.2. clang编译使用的环境如下: Ndk使用21.4 FFmpeg使用6.1 FFmpeg下载地址:https://ffmpeg.org/…

旗鼓相当的对手 - 加强版#洛谷

题目描述 现有 N ( N ≤ 1000 ) N(N\le 1000) N(N≤1000) 名同学参加了期末考试&#xff0c;并且获得了每名同学的信息&#xff1a;姓名&#xff08;不超过 8 8 8 个字符的字符串&#xff0c;没有空格&#xff09;、语文、数学、英语成绩&#xff08;均为不超过 150 150 15…

Spring Boot学习:Redis发布订阅

Redis发布订阅 Redis 2.8 及以上版本实现了发布订阅的功能&#xff0c;发送者可以通过 PUBLISH发布消息&#xff0c;订阅者通过 SUBSCRIBE 订阅接收消息或通过UNSUBSCRIBE 取消订阅。当发布者发布消息到指定频道时&#xff0c;所有订阅该频道的订阅者都能够接收到消息。这对于…

Halcon颜色通道的处理decompose3/image_to_channels/channels _to _image

Halcon颜色通道的处理 文章目录 Halcon颜色通道的处理一. 图像的通道二. 访问通道1.访问通道2.获取通道的数量 三. 通道分离与合并1. decompose3算子2. image_to_channels 算子3. compose3算子4. channels_to_image算子 四. 处理RGB信息 由于彩色图像通常包含不止一个通道&…

《基于 Vue 组件库 的 Webpack5 配置》- 总结

前言 Vue2 项目升级到 Webpack5 后&#xff0c;相关的配置也有所变化&#xff01;此篇以记录和总结&#xff0c;共同学习 Webpack ~ 推荐相关文章&#xff1a; 《Vue2.x 组件库 Webpack3 升 5》《Vue2.x 项目 Webpack 4 升级 5&#xff08;半自动升级&#xff09;》 配置 1…

LabVIEW开发LED驱动电源测试系统

LabVIEW开发LED驱动电源测试系统 本项建立一个二维激光振镜扫描控制系统&#xff0c;涵盖了光学系统和激光器的选择以及振镜驱动器的设计。项目的核心工作包括振镜驱动器的硬件电路设计、上位机控制软件的编写以及驱动器底层驱动软件的开发。此外&#xff0c;还对扫描图形的几何…

拍照就能建模!手机就能访问! 这个技术正成为宣传新手段!

随着人工智能技术的不断进步&#xff0c;现在可以通过拍摄照片结合AI技术来实现3D模型生成。这种技术的出现&#xff0c; 不仅能更加方便快捷地创建3D模型&#xff0c;而且还能真实复原现实中物件的质感、纹理等。同时&#xff0c;极大地降低了各行业对3D技术的应用门槛&#x…

中科院1区TOP,Elsevier出版社,均1-2个月录用!检索超稳!

【SciencePub学术】本期&#xff0c;小编给大家推荐的是一本Elsevier旗下、工程技术领域、影响因子为6.0的中科院1区TOP。其详情如下&#xff1a; 期刊简介 TRIBOLOGY INTERNATIONAL ISSN&#xff1a;0301-679X E-ISSN&#xff1a;1879-2464 IF&#xff08;2022&#x…

ES6+ 面试常问题

一、let const var 的区别 1. var&#xff1a; 没有块级作用域的概念&#xff0c;有函数作用域和全局作用域的概念全局作用域性下创建变量会被挂在到 windows 上存在变量提升同一作用域下&#xff0c;可以重复赋值创建未初始化&#xff0c;值为 undefined 2. let&#xff1a…

最新AI系统ChatGPT网站H5系统源码,支持AI绘画,GPT语音对话+ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

经典文献阅读之--OccNeRF(基于神经辐射场的自监督多相机占用预测)

0. 简介 作为基于视觉感知的基本任务&#xff0c;3D占据预测重建了周围环境的3D结构。它为自动驾驶规划和导航提供了详细信息。然而&#xff0c;大多数现有方法严重依赖于激光雷达点云来生成占据地面真实性&#xff0c;而这在基于视觉的系统中是不可用的。之前我们介绍了《经典…

【 ATU NXP-SBC 系列 】FS26XX GUI_OTP烧录与模拟操作

1. 概述 FS26XX 为了其安全性需求&#xff0c;针对重要暂存器的配置&#xff0c;使用 one time program 的功能&#xff0c;避免不小心修改重要暂存器&#xff0c;导致发生重大意外&#xff0c;使系统丧失功能安全性。FS26XX 也可以让使用者先测试 OTP 后的结果功能&#xff0…

微信小程序开发系列-03全局配置中的“window”和“tabBar”

微信小程序开发系列目录 《微信小程序开发系列-01创建一个最小的小程序项目》《微信小程序开发系列-02注册小程序》《微信小程序开发系列-03全局配置中的“window”和“tabBar”》《微信小程序开发系列-04获取用户图像和昵称》《微信小程序开发系列-05登录小程序》《微信小程序…

C# 如何使用?、? 和 ??的区别和使用案例

目录 ? 运算符 使用案例 ?? 运算符 使用案例 总结 在 C# 中&#xff0c;? 和 ?? 运算符在处理 null 值时起着不同的作用&#xff0c;并且具有特定的使用场景。 ? 运算符 ? 运算符&#xff0c;也称为空条件运算符&#xff0c;在 C# 6.0 及更高版本中引入。它允许…

08-React路由(Router 6版本)

Router5和Router6的变化 部分标签产生了变化&#xff0c;之前的标签都有了替&#xff08;主要集中在Route匹配上&#xff09;&#xff0c;所以这里先回顾一下Router5&#xff0c;同时引出Router6的一些新特性 其次&#xff0c;React官方在推出Router6之后&#xff0c;就明确推…