news 2026/5/5 20:37:26

SpringBoot中通过SpringCache线程优化(用Redis优化MySQL)(基于苍穹外卖项目)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot中通过SpringCache线程优化(用Redis优化MySQL)(基于苍穹外卖项目)

📚 目录(点击跳转对应章节)

  • 一、基础线程优化:不使用注解的手动缓存实现
    • 1.1 传统MySQL查询性能瓶颈分析
    • 1.2 手动Redis缓存实现方式
    • 1.3 线程安全问题及解决方案
  • 二、SpringBoot Cache线程优化注解详解
    • 2.1 Spring Cache核心注解介绍
    • 2.2 Spring Cache注解使用
  • 三、苍穹外卖项目中的实际应用案例
    • 3.1 项目中的实际使用
    • 3.2 ai给出的额外配置优化
    • 3.3 注意事项
  • 四、总结

一、基础线程优化:不使用注解的手动缓存实现

1.1 传统MySQL查询性能瓶颈分析

1.1.1直接数据库访问的性能问题

  • 数据库查询性能问题

以下是苍穹外卖小程序端进行菜品套餐类查询时访问数据库的操作情景

  • 缓存引入的必要性

以上的数据库操作还只是单人进行数据库查询的情况,当数据库访问量增大时,数据库查询性能问题就会成为问题,而缓存引入则是为了解决这个问题。

1.2 手动Redis缓存实现方式

  • 缓存查询逻辑手动编码
@AutowiredprivateRedisTemplateredisTemplate;/** * 根据分类id查询菜品 * * @param categoryId * @return */@GetMapping("/list")@ApiOperation("根据分类id查询菜品")publicResult<List<DishVO>>list(LongcategoryId){//构造redis中的key,规则:dish_分类idStringkey="dish_"+categoryId;//查询redis中是否存在菜品数据List<DishVO>list=(List<DishVO>)redisTemplate.opsForValue().get(key);if(list!=null&&list.size()>0){//如果存在,直接返回,无须查询数据库returnResult.success(list);}Dishdish=newDish();dish.setCategoryId(categoryId);dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品//如果不存在,查询数据库,将查询到的数据放入redis中list=dishService.listWithFlavor(dish);redisTemplate.opsForValue().set(key,list);returnResult.success(list);}

以上关于redis的java操作可以去看这篇文章,有详细讲解https://blog.csdn.net/yyzytx5201314/article/details/155777981?spm=1001.2014.3001.5502

经过优化后再次进行原来的查询操作时

当进行数据库查询时,数据库会进行查询,并返回查询结果,返回结果会进行缓存,下次再进行相同的查询操作时,会直接从缓存中获取数据,从而大大提高查询效率

1.3 线程安全问题及解决方案

  • 出现的问题

当mysql数据库中的数据改变时,而redis中缓存的数据未得到改变就会出现数据不一致的问题,因此我们要进行手动添加处理的方法

(这是进行的测试)

  • 解决方法
//在要解决缓存不一致的地方加入如上的操作@AutowiredprivateRedisTemplateredisTemplate;/** * 清理缓存数据 * @param pattern */privatevoidcleanCache(Stringpattern){Setkeys=redisTemplate.keys(pattern);redisTemplate.delete(keys);}
  • 新增菜品优化
/** * 新增菜品 * * @param dishDTO * @return */@PostMapping@Operation(summary="新增菜品")publicResultsave(@RequestBodyDishDTOdishDTO){log.info("新增菜品:{}",dishDTO);dishService.saveWithFlavor(dishDTO);//清理缓存数据Stringkey="dish_"+dishDTO.getCategoryId();cleanCache(key);returnResult.success();}
  • 菜品批量删除优化
/** * 批量删除菜品 * * @param ids * @return */@DeleteMapping@Operation(summary="批量删除菜品")publicResultdelete(@RequestParamList<Long>ids){log.info("批量删除菜品:{}",ids);dishService.deleteBatch(ids);//清理缓存数据cleanCache("dish_*");returnResult.success();}
  • 修改菜品优化
/** * 修改菜品 * * @param dishDTO * @return */@PutMapping@ApiOperation("修改菜品")publicResultupdate(@RequestBodyDishDTOdishDTO){log.info("修改菜品:{}",dishDTO);dishService.updateWithFlavor(dishDTO);//清理缓存数据cleanCache("dish_*");returnResult.success();}
  • 菜品起售停售优化
/** * 菜品起售停售 * * @param status * @param id * @return */@PostMapping("/status/{status}")@ApiOperation("菜品起售停售")publicResult<String>startOrStop(@PathVariableIntegerstatus,Longid){dishService.startOrStop(status,id);//清理缓存数据cleanCache("dish_*");returnResult.success();}

二、SpringBoot Cache线程优化注解详解

2.1 Spring Cache核心注解介绍

  • @Cacheable:缓存查询结果
  • @CachePut:更新缓存
  • @CacheEvict:清除缓存
  • @Caching:组合多个缓存操作

2.2 Spring Cache注解使用

  • 在启动类中添加@EnableCaching注解(开启缓存注解功能)
packagecom.itheima;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cache.annotation.EnableCaching;@Slf4j@SpringBootApplication@EnableCaching//开启缓存注解功能publicclassCacheDemoApplication{publicstaticvoidmain(String[]args){SpringApplication.run(CacheDemoApplication.class,args);log.info("项目启动成功...");}}
  • Cacheable注解
/** * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据, * 调用方法并将方法返回值放到缓存中 * value:缓存的名称,每个缓存名称下面可以有多个key * key:缓存的key */@GetMapping@Cacheable(cacheNames="userCache",key="#id")publicUsergetById(Longid){Useruser=userMapper.getById(id);returnuser;}

key的扩展:可以使用SpEL表达式来动态生成key,例如:key=“#user.id”,表示根据user对象的id属性作为key

  • CachePut注解
/** * CachePut:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据, * 调用方法并将方法返回值放到缓存中 * value:缓存的名称,每个缓存名称下面可以有多个key * key:缓存的key */@PutMapping@CachePut(cacheNames="userCache",key="#user.id")publicUserupdate(@RequestBodyUseruser){userMapper.update(user);returnuser;}
  • CacheEvict注解
/** * CacheEvict:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据, * 调用方法并将方法返回值放到缓存中 * value:缓存的名称,每个缓存名称下面可以有多个key * key:缓存的key */@DeleteMapping@CacheEvict(cacheNames="userCache",key="#id")publicResultdelete(Longid){userMapper.delete(id);returnResult.success();}

三、苍穹外卖项目中的实际应用案例

3.1 项目中的实际使用

  • service层的具体缓存应用场景
// DishServiceImpl.java - 菜品业务层实现类(节选关键缓存相关代码)@ServicepublicclassDishServiceImplimplementsDishService{// ... 其他代码 .../** * 新增菜品和对应的口味 * * @param dishDTO */@Transactional@CacheEvict(value="dishCache",key="#dishDTO.categoryId")@OverridepublicvoidsaveWithFlavor(DishDTOdishDTO){// ... 实现代码 ...}/** * 菜品批量删除 * * @param ids */@Transactional@CacheEvict(value="dishCache",allEntries=true)@OverridepublicvoiddeleteBatch(List<Long>ids){// ... 实现代码 ...}/** * 根据id修改菜品的基本信息和对应的口味信息 * @param dishDTO */@Transactional@CacheEvict(value="dishCache",allEntries=true)@OverridepublicvoidupdateWithFlavor(DishDTOdishDTO){// ... 实现代码 ...}/** * 菜品的起售和停售(关联套餐进行操作) * @param status * @param id */@Override@CacheEvict(value="dishCache",allEntries=true)publicvoidstartOrStop(Integerstatus,Longid){// ... 实现代码 ...}/** * 条件查询菜品和口味 * @param dish * @return */@Override@Cacheable(value="dishCache",key="#dish.categoryId")publicList<DishVO>listWithFlavor(Dishdish){// ... 实现代码 ...}// ... 其他代码 ...}

3.2 ai给出的额外配置优化

// RedisConfiguration.java - Redis缓存配置类@Configuration@Slf4jpublicclassRedisConfiguration{// ... 其他代码 ...@BeanpublicCacheManagercacheManager(RedisConnectionFactoryredisConnectionFactory){RedisCacheConfigurationcacheConfiguration=RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1))// 设置缓存过期时间为1小时.disableCachingNullValues();// 不缓存空值returnRedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();}}

3.3 注意事项

推荐将缓存操作加在到service层而不是controller层(黑马的课中写在了controller层,但是w 我经过查询发现service层更合适),这样service层与dao层之间的数据交互更清晰,controller层只负责接收请求与返回响应,使每个层职责更单一,更清晰,更易维护。
也更符合单一职责原则。

四、总结

在苍穹外卖项目中,通过引入 Redis 缓存显著优化了高频菜品查询的性能:从手动编码的“先查缓存、后查数据库、更新时主动清理”模式,到更优雅的 Spring Cache 注解方式(@Cacheable + @CacheEvict),有效解决了数据库压力大和缓存一致性问题。

核心收益:

  • 查询效率大幅提升。
  • 数据库负载降低,系统并发能力增强。
  • 代码更简洁、可维护性更强(推荐 Service 层使用注解)。

最佳实践建议:

  • 优先使用 Spring Cache 注解实现声明式缓存。
  • 对于精细化控制场景,可结合手动 Redis 操作。
  • 始终关注缓存一致性:更新/删除操作及时清除相关缓存,并合理设置过期时间。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 9:57:09

平台运营指南:新榜小豆芽指纹浏览器的专属安全方案

做抖音、小红书、视频号这类高风控平台运营&#xff0c;每天都在 “踩雷边缘” 试探 —— 多账号共用 IP 被批量限流&#xff0c;私信漏看错失变现机会&#xff0c;逆向方案突然失效导致账号登不上&#xff0c;甚至辛苦运营的账号直接被封。而同类工具只做基础账号管理&#xf…

作者头像 李华
网站建设 2026/4/30 13:29:38

Looki L1:当AI睁开“双眼”,感知物理世界的革命已然到来

各专栏更新如下&#x1f447; 大模型初探分享零基础AI学习经历 OAI-5G开源通信平台实践 OpenWRT常见问题分析 5G CPE 组网技术分享 Linux音视频采集及视频推拉流应用实践详解 得力工具提升工作效率 Looki L1&#xff1a;当AI睁开“双眼”&#xff0c;感知物理世界的革命已…

作者头像 李华
网站建设 2026/5/4 13:44:37

HTML可视化监控TensorRT推理过程中的GPU利用率

HTML可视化监控TensorRT推理过程中的GPU利用率 在部署深度学习模型到生产环境时&#xff0c;开发者常常面临一个棘手的问题&#xff1a;明明模型结构没有变化&#xff0c;为什么实际推理延迟居高不下&#xff1f;吞吐量始终上不去&#xff1f;这时候&#xff0c;仅仅看日志或跑…

作者头像 李华
网站建设 2026/4/21 21:45:53

“不会UI设计”的程序员,如何用AI做出让小朋友着迷的界面?

作为一名个人开发者&#xff0c;从零到一实现软件全部功能是家常便饭。很多朋友已经能熟练运用 AI 编程助手完成功能开发&#xff0c;可一到软件界面设计&#xff0c;就犯了难——即便有 shadcn/ui、radix-ui 或 magic UI 这类现成的组件库&#xff0c;依然不知如何下手。框架搭…

作者头像 李华
网站建设 2026/4/19 15:11:27

python一种基于Java的校园二手物品置换系统设计与实现_a7801473_tn007-pycharm Vue django flask项目源码

目录已开发项目效果实现截图关于我系统介绍开发技术路线核心代码参考示例本项目开发思路结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;已开发项目效果实现截图 同行可拿货,招校园代理 python一种基于Java的校园二手物品置换系统设…

作者头像 李华