您现在的位置是:首页 >技术杂谈 >缓存优化----SpringCache网站首页技术杂谈

缓存优化----SpringCache

陈毓辰 2023-06-08 00:00:02
简介缓存优化----SpringCache

spring cache

  • spring Cache介绍

    spring cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。

    Spring cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。

    cacheManager是spring提供的各种缓存技术抽象接口

    针对不同的缓存技术需要实现不同的cacheManager:

    CacheManager描述
    EhCacheCacheManager使用EhCache作为缓存技术
    GuavaCacheManager使用Google的GuavaCache作为缓存技术
    RedisCacheManager使用Redis作为缓存技术
  • spring cache常用注解

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

    在Springboot项目中,使用缓存技术只需在项目中导入相关的缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存技术支撑即可

    例如:使用redis作为缓存技术,只需要导入spring data redis的maven坐标即可

  • spring cache使用方式

    在springboot项目中使用springcache的操作步骤(使用redis缓存技术):

    1. 导入maven坐标

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-cache</artifactId>
      </dependency>
      
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
    2. 配置application.yml

      redis:
        host: 172.17.2.94
        port: 6379
        password: root@123456
        database: 0
      cache:
        redis:
          time-to-live: 1800000 #设置缓存过期时间,可选
      
  1. 在启动类上加上@EnableCaching注解,开启缓存注解功能
  2. 在Controller的方法上加入@Cacheable、@CacheEvict等注解,进行缓存操作

userController

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

    /**
     * CachePut:将方法返回值放入缓存
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     */
    @CachePut(value = "userCache",key = "#user.id")
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

    /**
     * CacheEvict:清理指定缓存
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     */
    @CacheEvict(value = "userCache",key = "#p0")//p0代表第一个参数,id是第一个参数等价
    //@CacheEvict(value = "userCache",key = "#root.args[0]")
    //@CacheEvict(value = "userCache",key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }

    //@CacheEvict(value = "userCache",key = "#p0.id")
    //@CacheEvict(value = "userCache",key = "#user.id")
    //@CacheEvict(value = "userCache",key = "#root.args[0].id")
    @CacheEvict(value = "userCache",key = "#result.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

    /**
     * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     * condition:条件,满足条件时才缓存数据
     * unless:满足条件则不缓存
     */
//    @Cacheable(value = "userCache",key = "#id",condition = "#result != null")//在源码中不能使用result这个参数,而unless可以使用result
    @Cacheable(value = "userCache",key = "#id",unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

    @Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }
}

pom.cml

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

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

缓存套餐数据

  • 实现思路

前面我们已经实现了移动端套餐查看功能,对应的服务端方法为SetmealController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频道查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统性能。

具体的实现思路如下:

  1. 导入springcache和redis相关maven坐标

在这里插入图片描述

  1. 在application.yml中配置缓存数据的过期时间

在这里插入图片描述

  1. 在启动类上加入@EnableCaching注解,开启缓存注解功能

在这里插入图片描述

  1. 在SetmealController的list方法上加入@Cacheable注解

注意:返回值无法序列化 报错: DefaultSerializer requires a Serializable payload but received an object of type [com.itheima.reggie.common.R]

解决:

public class R<T> implements Serializable {//将R进行序列化不然进行SpringCache的时候会报错

报错:RedisCommandExecutionException: ERR wrong number of arguments for 'set' command

原因:redis版本原因,版本太低了,更换一个高点的redis版本就可以解决

在这里插入图片描述

  1. 在SetmealController的save和delete方法上加入CacheEvict注解
  • 代码改造
/*
* 删除套餐
* */
@DeleteMapping
@CacheEvict(value = "setmealCache",allEntries = true)//allEntries = true 该代码的意思是清除缓存中所有的数据
public R<String> delete(@RequestParam List<Long> ids){
    log.info("ids:{}",ids);
    setmealService.removeWithDish(ids);
    return R.success("套餐数据删除成功!!!!");
}
/*
    * 用户端套餐展示,根据条件查询套餐数据
    * */
    @GetMapping("/list")
    @Cacheable(value = "setmealCache",key = "#setmeal.categoryId+'_'+#setmeal.status")
    public R<List<Setmeal>> list(Setmeal setmeal){
        LambdaQueryWrapper<Setmeal>queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(setmeal.getCategoryId()!=null,Setmeal::getCategoryId,setmeal.getCategoryId());
        queryWrapper.eq(setmeal.getStatus()!=null,Setmeal::getStatus,setmeal.getStatus());
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);
        List<Setmeal> list = setmealService.list(queryWrapper);

        return R.success(list);
    }

  • 功能测试

在这里插入图片描述

问题(报错解决)

报错:RedisCommandExecutionException: ERR wrong number of arguments for 'set' command

原因:redis版本原因,版本太低了,更换一个高点的redis版本就可以解决

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。