news 2026/7/13 2:18:42

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/7/11 13:28:08

COLMAP动态干扰消除:从问题诊断到智能解决方案

COLMAP动态干扰消除&#xff1a;从问题诊断到智能解决方案 【免费下载链接】colmap COLMAP - Structure-from-Motion and Multi-View Stereo 项目地址: https://gitcode.com/GitHub_Trending/co/colmap 在处理街景、室内场景或公共场所的3D重建时&#xff0c;COLMAP动态…

作者头像 李华
网站建设 2026/7/12 0:30:27

SGMICRO圣邦微 SGM2006-2.8XN5/TR SOT23-5 线性稳压器(LDO)

特性 低输出噪声:30uVrms类型(10Hz至100kHz)超低压差电压: 输出150mA时为150mV低负载时供电电流为77μA 在150mA输出时&#xff0c;低功耗工作电流为150μA 高电源抑制比:在1kHz时为73dB-热过载保护 输出电流限制 预设输出电压(精度 士2.7%) -10纳安逻辑控制关断提供多种输出电…

作者头像 李华
网站建设 2026/7/11 18:06:14

19、PC-BSD社区求助资源全攻略

PC-BSD社区求助资源全攻略 在参与任何在线社区时,都要牢记一些规则。了解基本的网络礼仪后,下面为大家介绍PC - BSD社区中可用的求助资源。 PC - BSD论坛 对于新用户来说,PC - BSD论坛可能是最佳的起点。通过网页浏览器就能访问该论坛,无需安装额外软件。在这里,搜索答案…

作者头像 李华
网站建设 2026/7/10 20:04:26

5分钟搞定F5-TTS语音合成:从零配置到实战应用完整指南

5分钟搞定F5-TTS语音合成&#xff1a;从零配置到实战应用完整指南 【免费下载链接】F5-TTS Official code for "F5-TTS: A Fairytaler that Fakes Fluent and Faithful Speech with Flow Matching" 项目地址: https://gitcode.com/gh_mirrors/f5/F5-TTS 还在为…

作者头像 李华
网站建设 2026/7/11 18:06:12

Qwen3-8B与14B的TTFT性能对比及优化原理

Qwen3-8B与14B的TTFT性能对比及优化原理 在当前大模型部署日益普及的背景下&#xff0c;用户对响应速度的要求越来越高——尤其是在智能客服、实时对话系统等高交互场景中&#xff0c;“快”往往比“大”更重要。通义实验室发布的Qwen3系列模型中&#xff0c;Qwen3-8B 和 Qwen…

作者头像 李华
网站建设 2026/7/10 18:54:39

Miniconda + conda-forge:AI开发环境管理指南

Miniconda conda-forge&#xff1a;AI开发环境管理指南 &#x1f9ed; 你有没有过这样的经历&#xff1f;深夜两点&#xff0c;终于跑通了某个论文的代码&#xff0c;兴奋地发给同事演示&#xff0c;结果对方回了一句&#xff1a;“我这边报错了。” 或者更糟——你在自己的机…

作者头像 李华