数据缓存(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.hqwc.cn/news/308261.html

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

相关文章

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus 1. 根的作用2. 手绘技巧3. 分离点/汇合点&根轨迹的几何性质 1. 根的作用 G ( s ) s 3 s 2 2 s 4 G\left( s \right) \frac{s3}{s^22s4} G(s)s22s4s3​…

线上隐私保护的未来:分布式身份DID的潜力

在日益数字化的世界中&#xff0c;人们的生活越来越多地依赖于互联网&#xff0c;数字身份也因而变得越来越重要。根据法律规定&#xff0c;互联网应用需要确认用户的真实身份才能提供各种服务&#xff0c;而用户则希望在进行身份认证的同时能够尽量保护他们的个人隐私&#xf…

QT UI自动化测试(1)

一、框架选择 想结合公司产品搭建一套自动化测试框架&#xff0c;一方面自己学习用&#xff0c;一方面也希望跟公司业务结合起来&#xff0c;双赢。公司软件最多的产品是部署在Linux系统上&#xff0c;基于QT QML开发的UI&#xff0c;本来奔着免费的自动化框架去的&#xff0c;…

【北亚服务器数据恢复】san环境下LUN Mapping出错导致文件系统一致性出错的数据恢复案例

服务器数据恢复环境&#xff1a; san环境下的存储上一组由6块硬盘组建的RAID6&#xff0c;划分为若干LUN&#xff0c;MAP到跑不同业务的服务器上&#xff0c;服务器上层是SOLARIS操作系统UFS文件系统。 服务器故障&#xff1a; 业务需求需要增加一台服务器跑新增的应用&#xf…

CodeWhisperer:编码世界中的声音启迪者

人烟 导语&#xff1a; 在数字化时代&#xff0c;编码已经成为了一种不可或缺的技能。而 CodeWhisperer&#xff08;编码世界中的声音启迪者&#xff09;则以其卓越的技术和深厚的知识为人们带来了独特的启发和指导。本文将介绍 CodeWhisperer 的背景和成就&#xff0c;探讨他是…

python/selenium/jenkins整合

1、新建python项目&#xff0c;专门写selenium代码&#xff0c;建议用pytest框架写。 2、把代码上传到代码库中。 3、环境配置&#xff1a; 3.1 在跑jenkins的机器上配置好python环境&#xff0c;需要python --version能在任何地方运行&#xff08;配置好系统环境变量&#…

使用element中el-cascader级联选择器实现省市区街道筛选(非动态加载)

<template><el-form ref"form" :model"form" label-width"80px"><el-form-item label"地址:" prop"addressList"><el-cascaderv-model"form.addressList":props"props":options&q…

封装uniapp签字板

新开发的业务涉及到签字功能&#xff0c;由于是动态的表单&#xff0c;无法确定它会出现在哪里&#xff0c;不得已封装模块。 其中涉及到一个难点就是this的指向性问题&#xff0c; 第二个是微信小程序写法&#xff0c; 我这个写法里用了u-view的写法&#xff0c;可以自己修改组…

Android Studio修改创建新布局时默认根布局

将Android Studio默认布局ConstraintLayout切换成LinearLayout 打开设置&#xff0c; Editor> File and Code Templates > Other > layoutResourceFile.xml 备注&#xff1a;创建时提示根布局仍然为ConstraintLayout&#xff0c;不用管这个&#xff0c;实际创建的…

【Linux系统化学习】进程终止的奥秘

个人主页点击直达&#xff1a;小白不是程序媛 Linux专栏&#xff1a;Liunx系统化学习 代码仓库&#xff1a;Gitee 目录 获取函数返回值 退出码 进程退出的场景 错误码 信号终止异常代码 进程的终止 main函数直接return exit函数 _exit函数 获取函数返回值 在C语言学…

测试开发工具推荐(含自动化、性能、稳定性、抓包)

今天将给大家推荐14款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、APP性能测试、稳定性测试、抓包工具等。 一、UI自动化测试工具 1. uiautomator2 Github地址 https://github.com/openatx/uiautomator2 介绍&#xff1a; openatx开源的ui自动化…

Vscode新手安装与使用

安装与版本选择 VS Code 有两个不同的发布渠道&#xff1a;一个是我们经常使用的稳定版&#xff08;Stable&#xff09;&#xff0c;每个月发布一个主版本&#xff1b;另外一个发布渠道叫做 Insiders&#xff0c;每周一到周五 UTC 时间早上6点从最新的代码发布一个版本&#x…