深入解析:苍穹外卖-Day7(缓存菜品/套餐,清除缓存)
(一)缓存菜品
在大量用户访问的高并发场景下(如外卖平台),系统面临每秒数万甚至数十万次请求时,传统数据库(如 MySQL)难以承受直接压力,可能导致响应延迟、服务崩溃。
Redis是通过键值对实现的,对于高并发读 / 写,他的操作复杂度为O(1)。能够有效应对高并发问题。

上图中体现了缓存菜品的实现流程:当前端请求菜品数据时,首先检查缓存(Redis)中是否存在目标数据,存在则直接使用缓存中数据进行返回,不查询数据库;不存在则查询数据库返回数据,并将数据保存在缓存中。
代码实现:在用户菜品接口的Controller层实现缓存查询操作:
package com.sky.controller.user;
import com.sky.constant.StatusConstant;
import com.sky.entity.Dish;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private RedisTemplate
(二)清除菜品缓存,保证数据库和缓存中的数据一致性
当管理端修改菜品信息时,此时实时改变的只是数据库数据。会导致缓存中数据与修改后数据库中数据不一致,为了解决这个问题,需要在菜品信息变动时,删除缓存,保证数据一致性。
代码实现:在管理端的菜品接口Controller层实现缓存删除操作
核心代码:
/*清除缓存方法*/ public void DeleteRedis(String pattern){ //Redis不支持直接通过匹配模式进行删除,只能删除一个或多个key //先使用Redis提供的keys()方法根据匹配模式获取对应的键,返回的是一个Set集合(无序) Set keys=redisTemplate.keys(pattern); redisTemplate.delete(keys); }//调用方法进行删除缓存
//eg1:修改菜品可能之前属于A分类,修改后属于B分类,难以确定,直接全部删除 String pattern="dish_*"; //*为通配符,匹配任何模式 DeleteRedis(pattern);//eg2:仅删除一个分类
String pattern="dish_"+dishDTO.getCategoryId(); //调用方法实现清除 DeleteRedis(pattern);
package com.sky.controller.admin;
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.mapper.DishMapper;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
@Slf4j
@RestController
@Api(tags = "菜品相关接口")
@RequestMapping("/admin/dish")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private RedisTemplate
//出现问题:能够添加缓存,但是修改菜品信息后无法清除缓存数据?
解决:在Redis的配置类中要实现序列化,才能正确匹配模式,且要记得添加@Bean将配置类交给SpringBoot进行管理,否则设置的序列化无法被使用:
redis 中我存入了数据,为什么获取不到_redis存进去了但是读不到-CSDN博客
package com.sky.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/*配置类实现对Redis配置*/
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){//注入Redis的连接工厂对象
log.info("开始创建Redis模板对象");
RedisTemplate redisTemp = new RedisTemplate();
//设置Redis的连接工厂对象(这个连接工厂对象 是由Starter创建好放入Spring容器中)
redisTemp.setConnectionFactory(redisConnectionFactory);
// //设置redis key 的序列化器
// redisTemp.setKeySerializer(new StringRedisSerializer());
// **设置 Key 使用 String 序列化,避免 JDK 序列化**
redisTemp.setKeySerializer(new StringRedisSerializer());
//redisTemp.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// **设置 HashKey 和 HashValue 也使用 String 方式存储**
redisTemp.setHashKeySerializer(new StringRedisSerializer());
// redisTemp.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemp.afterPropertiesSet();
return redisTemp;
}
}
(三)使用Spring cache缓存套餐
通过在相应的方法上添加缓存注解,可以使用Spring cache ,进一步简化缓存代码的开发


(3.1)Spring cache的入门案例1
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
//-------------添加注解实现缓存存储--------------------------
// @CachePut(cacheNames="usercache",key="abc") //此时存储到spring cache中的key为usercache::abc
@CachePut(cacheNames="usercache",key="#categoryId") //此时存储到spring cache中的key为usercache::categoryId //spring EL利用反射机制来访问对象的属性,调用方法等,实现动态存储
public Result> list(Long categoryId) {
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List list = dishService.listWithFlavor(dish);
return Result.success(list);
}
: 表示下一层(cache也有树结构)
(1)@CachePut(cacheNames="usercache",key="#categoryId") //此时存储到spring cache中的key为usercache::categoryId //spring EL利用反射机制来访问对象的属性,调用方法等,实现动态存储
(2)@CachePut(cacheNames="usercache",key="#result") //result表示当前方法的返回值
若是返回值为User对象,其中有id属性可以写为@CachePut(cacheNames="usercache",key="#result.id")
(3)@CachePut(cacheNames="usercache",key="#p0") p0表示取当前行参中的第一个参数,p1表示第二个,依次类推;
(2)删除缓存
@CacheEvict(cacheNames="usercache", allEntries=true)//删除usercache下所有键值对
(2) CacheEvict(cacheNames="usercache", key="#result.id")删除指定键值对
浙公网安备 33010602011771号