使用Redis缓存功能解决用户量很大时,大量请求导致的数据库访问压力过大,性能低下问题
场景:使用Redis缓存菜品数据
@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private RedisTemplate redisTemplate;
/**
* 根据分类id查询菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
//查询redis中是否存在菜品数据
String key = "dish_" + categoryId;//key的形式:dish_13
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
//若存在,无需查询数据库,直接返回
if (list != null && list.size() > 0) {
return Result.success(list);
}
//若不存在,查询数据库,并将数据存入redis
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
list = dishService.listWithFlavor(dish);
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}
}
为保证数据库中的数据与Redis缓存数据保持一致,服务端操作菜品数据后需要清理被影响的缓存数据
@Slf4j
@RestController
@Api(tags = "菜品相关接口")
@RequestMapping("admin/dish")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private RedisTemplate redisTemplate;
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO) {
log.info("新增菜品");
dishService.saveWithFlavor(dishDTO);
//清理被影响数据的缓存
cleanCache("dish_" + dishDTO.getCategoryId());
return Result.success();
}
@ApiOperation("分页查询菜品")
@GetMapping("page")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {
log.info("菜品分页查询:{}", dishPageQueryDTO);
PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
return Result.success(pageResult);
}
@ApiOperation("根据id查询菜品")
@GetMapping("{id}")
public Result<DishVO> selectById(@PathVariable Long id) {
log.info("根据id查询菜品:{}", id);
DishVO dishVO = dishService.selectById(id);
return Result.success(dishVO);
}
@PutMapping
@ApiOperation("更新菜品")
public Result update(@RequestBody DishDTO dishDTO) {
log.info("更新菜品:{}", dishDTO);
dishService.update(dishDTO);
//清理所有数据的缓存
cleanCache("dish_*");
return Result.success();
}
@DeleteMapping
@ApiOperation("批量删除菜品")
public Result deleteByIds(@RequestParam List<Long> ids) {
log.info("批量删除菜品:{}", ids);
dishService.deleteByIds(ids);
//清理所有数据的缓存
cleanCache("dish_*");
return Result.success();
}
@PostMapping("status/{status}")
@ApiOperation("启售停售菜品")
public Result startOrStop(@PathVariable Integer status, Long id) {
log.info("启售停售菜品:{}", status, id);
dishService.startOrStop(status, id);
//清理所有数据的缓存
cleanCache("dish_*");
return Result.success();
}
@GetMapping("list")
@ApiOperation("动态条件名查询菜品")
public Result<List<Dish>> list(Dish dish) {
log.info("动态条件名查询菜品:{}", dish);
dish.setStatus(StatusConstant.ENABLE);
List<Dish> dishList = dishService.list(dish);
return Result.success(dishList);
}
/**
* 清理缓存数据
*
* @param pattern
*/
private void cleanCache(String pattern) {
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
}
SpringCache框架实现了基于注解的缓存功能
其底层提供了一层抽象,可以切换不同的缓存实现,eg:EHCache,Caffeine,Redis等
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId> <version>2.7.3version>
dependency>
@EnableCaching 开启缓存注解功能,常用于启动类上
@Cacheable 执行方法前先查询缓存中是否有需要的数据,若则直接返回缓存中的数据,若没有则查询数据库,并将返回值加入缓存中
@Cacheput 将方法的返回值放入缓存中
@CacheEvict 将一条或多条数据从缓存中删除
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
);
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.3version>
<relativePath/>
parent>
<groupId>com.sihfangroupId>
<artifactId>springcache-demoartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>11maven.compiler.source>
<maven.compiler.target>11maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.76version>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.1version>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
<version>3.0.2version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.7.3version>
plugin>
plugins>
build>
project>
server:
port: 8888
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring_cache_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
redis:
host: localhost
port: 6379
password: root
database: 1
logging:
level:
com:
itheima:
mapper: debug
service: info
controller: info
@Slf4j
@SpringBootApplication
@EnableCaching//开启缓存注解功能功能
public class CacheDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CacheDemoApplication.class,args);
log.info("项目启动成功...");
}
}
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private UserMapper userMapper;
/*
@CachePut:将方法的返回值放入缓存中去
使用Spring Cache缓存数据,Redis中key的生成方式:cacheNames::key,eg:userCache::user.id
key的写法支持SpEL语法,以#开头,后面跟方法参数名即可访问对应方法参数
.为对象导航,用于访问对象的属性
也可使用#result.id访问方法返回值对象的id属性
或者使用方法参数索引访问,如#p0,#a0,#root.args[0],均表示方法的第一个参数
*/
@PostMapping
//@CachePut(cacheNames = "userCache", key = "#result.id")
//@CachePut(cacheNames = "userCache", key = "#p0.id")
//@CachePut(cacheNames = "userCache", key = "#a0.id")
//@CachePut(cacheNames = "userCache", key = "#root.args[0].id")
@CachePut(cacheNames = "userCache",key = "#user.id")//对象插入完毕后会返回主键,user中id属性就有了值
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
/**
* @CacheEvict :将一条或多条数据从缓存中删除
* @param id
*/
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")
public void deleteById(Long id){
userMapper.deleteById(id);
}
/**
* 删除所有数据
*/
@CacheEvict(cacheNames = "userCache",allEntries = true)
@DeleteMapping("/delAll")
public void deleteAll(){
userMapper.deleteAll();
}
/**
@Cacheable :方法执行前先查询缓存中是否有数据,有则直接返回,
没有则调用方法并将方法的返回值存入缓存中
*/
@GetMapping
@Cacheable(cacheNames = "userCache",key = "#id")
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
}
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private int age;
}
package com.itheima.mapper;
import com.itheima.entity.User;
import org.apache.ibatis.annotations.*;
@Mapper
public interface UserMapper{
@Insert("insert into user(name,age) values (#{name},#{age})")
@Options(useGeneratedKeys = true,keyProperty = "id")
void insert(User user);
@Delete("delete from user where id = #{id}")
void deleteById(Long id);
@Delete("delete from user")
void deleteAll();
@Select("select * from user where id = #{id}")
User getById(Long id);
}