一.概述
注意:对于需要进行缓存的数据,必须要在它的类内定义序列版本号⭐。因为缓存是对数据的序列化和反序列化,不设置会导致类变更而无法反序列化
1.介绍
对短期访问量大且结果相同的数据请求而言,可以将其进行缓存,进而减轻服务器硬盘IO的压力。数据的缓存常常使用的是Redis数据库,即将数据由对硬盘的IO读取转为对内存的IO读取,提高访问速率,也避免了无意义的重复业务
2.实现思路
将硬盘读取的IO操作,转到内存中,提高了效率
通过Redis来缓存菜品数据,减少数据库查询操作
![image-20221208180818572](https://honescos.oss-cn-hangzhou.aliyuncs.com/img/image-20221208180818572HpIOxqeXkB20240316123774def088aadbdbd9.png)
3.方案
- 1.直接通过Redis客户端在特定的业务下对数据进行缓存、清理缓存操作
- 2.使用SpringCache,采用注解的形式进行数据的缓存、清理缓存操作
二.Redis客户端
1.缓存
(1)步骤
- 根据约定构建key
- 查询Redis数据库中是否存在菜品数据
- 存在:直接返回
opsForValue().get(key)
的结果 - 不存在:先进行查询,再使用
opsForValue().set(key,value)
缓存到Redis数据库中
- 存在:直接返回
(2)示例
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result> list(Long categoryId) {
//构造redis中的key,规则:dish_分类id
String key = "dish_" + categoryId;
//查询redis中是否存在菜品数据
List list = (List) 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);//将查询数据库的结果存入Redis
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}
2.清理缓存
(1)简述
**清理原因:**为了保持数据库和Redis中数据的一致性,我们需要在数据库数据更新的时候,对Redis中旧有的数据进行清理,让其重新进行缓存,从而保证数据的一致性
清理情况: 对数据库进行写操作的方法都要加上Redis清理方法,然后根据情况决定是要精细的清除其中一个还是将全部进行清理
(2)步骤
表达式就是Redis操作指令中keys的那个表达式,可以使用通配符
- 先执行
redisTemplate.keys()
根据传入的表达式查询出要删除的key的集合 - 再执行
redisTemplate.delete()
删除上面获取到的key集合
示例方法:
private void ClearCache(String pattern) {
//调用RedisTemplate查询传入的pattern对应的key,得到key的集合
Set keys = redisTemplate.keys(pattern);//pattern可以是一个也可以是多个,效果一样的
if (keys != null) {
redisTemplate.delete(keys);
}
}
(3)示例
/**
* 修改菜品
*
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO) {
log.info("修改菜品:{}", dishDTO);
dishService.updateWithFlavor(dishDTO);
//将所有的菜品缓存数据清理掉,所有以dish_开头的key
cleanCache("dish_*");
return Result.success();
}
三.Spring Cache
同一个业务中cacheNames请保持一致
1.概述
**简述:**Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
**缓存实现的切换:**Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现
- Spring会根据Pom中导入的客户端而自动采用相应的缓存实现,不需要额外配置
- EHCache
- Caffeine
- Redis(常用)
SPEL语法:
这个主要用于下面使用Spring Cache注解时设置Key的值
-
类内元素:
#类名.成员
获取参数列表中的id的值
@Cacheable(cacheNames = "userCache",key="#id") public User getById(Long id){}
-
单个元素:
#元素名
访问参数列表中user对象内的id成员的值
@CachePut(value = "userCache", key = "#user.id") public User save(@RequestBody User user){}
起步依赖:
org.springframework.boot
spring-boot-starter-cache 2.7.3
2.注解
注解 | 说明 |
---|---|
@EnableCaching | 开启缓存注解功能,通常加在启动类上 (开启缓存支持) |
@Cacheable | 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 (又放又取) |
@CachePut | 将方法的返回值放到缓存中 (只放不取) |
@CacheEvict | 将一条或多条数据从缓存中删除 (清理缓存) |
(1)EnableCaching
该注解是附加在启动类上用于开启缓存注解功能的
@Slf4j
@SpringBootApplication
@EnableCaching//开启缓存注解功能
public class CacheDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CacheDemoApplication.class,args);
log.info("项目启动成功...");
}
}
(2)Cacheable
作用: 在方法执行前,spring先查看缓存中是否有数据,
-
如果有数据,则直接返回缓存数据;
-
若没有数据,调用方法并将方法返回值放到缓存中
参数:
-
cacheNames:缓存的名称,每个缓存下可以有多个key
- 获取、删除的时候需要保证缓存名称都一致,否则会出问题
-
key: 缓存的key
使用Spring的表达式语言SPEL语法指定(详见下面附录)
格式:
@Cacheable(cacheNames = "缓存名称",key="缓存的key")
示例:
@GetMapping
@Cacheable(cacheNames = "userCache",key="#id")
public User getById(Long id){
User user = userMapper.getById(id);//根据ID获取缓存,当缓存中没有时,才会去调用Mapper查询数据
return user;
}
(3)CachePut
作用: 将方法返回值,放入缓存
参数:
-
value:缓存的名称,每个缓存下可以有多个key
- 获取、删除的时候需要保证缓存名称都一致,否则会出问题
-
key: 缓存的key
使用Spring的表达式语言SPEL语法指定(详见下面附录)
格式:
@CachePut(value = "缓存名称", key = "缓存的key")
示例:
@PostMapping
@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
(4)CacheEvict
**作用:**清理指定的缓存
参数:
-
cacheNames:缓存的名称,每个缓存下可以有多个key
- 获取、删除的时候需要保证缓存名称都一致,否则会出问题
-
key: 缓存的key
使用Spring的表达式语言SPEL语法指定
-
allEntries:该属性可以指定删除Redis中的所有缓存
- 和key不共存,二者只能出现一个
格式:
1.删除某个key对应的缓存数据
@CacheEvict(cacheNames = "缓存名称",key = "缓存的key")
2.删除指定cacheNames下的所有缓存数据
@CacheEvict(cacheNames = "缓存名称",allEntries = true)
示例:
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据
public void deleteById(Long id){
userMapper.deleteById(id);
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据
public void deleteAll(){
userMapper.deleteAll();
}