【 苍穹外卖day7 | 缓存 】

发布时间:2026/11/5 16:03:29

【 苍穹外卖day7 | 缓存 】 前言用户端浏览菜品时同一个分类会被反复查询如果每次请求都访问数据库菜品和口味的组装逻辑会重复执行。苍穹外卖在这里把分类菜品列表放进 Redis查询优先读取缓存后台修改菜品后再清理对应缓存保证用户端能拿到新数据。day07 还引入了 Spring Cache。它把缓存读写封装到注解中适合规则清晰的查询接口。本文结合项目现有代码把两种实现串起来。一、先看项目中的 Redis 配置项目在 sky-server 模块中引入了 spring-boot-starter-data-redis 和 spring-boot-starter-cache。连接信息由 application.yml 从当前环境配置中读取RedisConfiguration 再创建 RedisTemplate。BeanpublicRedisTemplateredisTemplate(RedisConnectionFactoryredisConnectionFactory){RedisTemplateredisTemplatenewRedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(newStringRedisSerializer());returnredisTemplate;}这里给 key 配置 StringRedisSerializerRedis 中的键会以可读字符串保存例如 dish_12后续使用 keys(“dish_*”) 按模式清理时也更容易定位。Spring Data Redis 官方文档说明RedisTemplate 的默认序列化器会处理对象读写而 key、value、hash field 可以分别配置序列化器。配置文件中的 Redis 密码、地址等属于环境信息提交代码或写博客时应使用占位符或脱敏值不能直接暴露真实配置。二、菜品列表缓存命中直接返回未命中再查询数据库用户端接口是 GET /user/dish/list参数为分类 id。缓存 key 按分类维度构造规则是 dish_分类 id不同分类的数据彼此隔离。GetMapping(/list)publicResultListDishVOlist(LongcategoryId){Stringkeydish_categoryId;ListDishVOlist(ListDishVO)redisTemplate.opsForValue().get(key);if(list!nulllist.size()0){returnResult.success(list);}DishdishnewDish();dish.setCategoryId(categoryId);dish.setStatus(StatusConstant.ENABLE);listdishService.listWithFlavor(dish);redisTemplate.opsForValue().set(key,list);returnResult.success(list);}这段代码分为两次访问路径缓存命中opsForValue().get(key) 取到列表后直接响应不进入 Service 和数据库。缓存未命中按分类 id 和起售状态查询菜品再把结果写回 Redis下一次同分类请求即可命中缓存。Service 层的 listWithFlavor 不只是查询菜品表还会按菜品 id 查询口味并封装成 DishVO。因此缓存保存的是已经组装好的菜品列表避免每次浏览菜单都重复查询口味。Spring Data Redis 中opsForValue() 对应 Redis 的字符串值操作当前代码用 get 读取、用 set 写入key 必须非空。项目使用原始类型 RedisTemplate所以从缓存取出 List 时出现了强制转换实际开发中可以优先声明 RedisTemplateString, Object 或按项目统一的序列化方案约束数据类型。三、缓存更新菜品变更后必须清理旧数据缓存只解决读性能数据变更后的旧缓存同样需要处理。后台菜品管理接口在新增、修改、删除和起售停售后都会调用 cleanCache。privatevoidcleanCache(Stringpattern){SetkeysredisTemplate.keys(pattern);redisTemplate.delete(keys);}新增菜品只影响当前分类因此按 dish_ 分类 id 清理修改、批量删除、起售停售可能影响多个分类或列表展示当前项目统一清理 dish_*。// 新增菜品后清理当前分类Stringkeydish_dishDTO.getCategoryId();cleanCache(key);// 修改、删除、起售停售后清理全部菜品缓存cleanCache(dish_*);下一次用户端请求会发现 Redis 中没有对应 key于是回源数据库并写入最新列表。这种“更新数据库后删除缓存、读取时再填充缓存”的方式代码路径直观也能避免用户端长期拿到旧菜品图片、价格或状态。需要注意keys(pattern) 会按模式扫描键。当前课程项目的键数量很少便于理解和实现在键空间很大的生产环境中应评估扫描成本并根据缓存前缀、批量删除策略或更精确的 key 设计控制影响范围。四、店铺营业状态Redis 也适合保存轻量状态项目还使用固定 key shop_status 保存营业状态。管理员接口写入 1 或 0用户端接口读取该值当 Redis 中还没有该 key 时代码默认返回打烊状态。// 管理端设置状态redisTemplate.opsForValue().set(shop_status,status);// 用户端获取状态Integerstatus(Integer)redisTemplate.opsForValue().get(shop_status);if(statusnull){statusStatusConstant.DISABLE;}营业状态由管理员直接维护为一份共享状态不需要先查询数据库再填充列表缓存。两者都使用 RedisTemplate但 key 的设计和写入时机要随着业务含义变化。五、Spring Cache把套餐列表缓存写进注解项目启动类已经添加 EnableCaching开启缓存注解支持。用户端套餐列表使用 Cacheable调用方法前会先根据缓存名称和 key 查询缓存命中时直接返回没有缓存时才执行方法并将返回值缓存起来。GetMapping(/list)Cacheable(cacheNamessetmealCache,key#categoryId)publicResultListSetmeallist(LongcategoryId){SetmealsetmealnewSetmeal();setmeal.setCategoryId(categoryId);setmeal.setStatus(StatusConstant.ENABLE);ListSetmeallistsetmealService.list(setmeal);returnResult.success(list);}cacheNames 是缓存名称key 使用 SpEL 表达式 #categoryId表示取方法参数 categoryId 作为 key。按项目代码注释生成的 Redis key 形式为 setmealCache::分类 id。day07 中的另外两个常用注解可以这样理解注解执行时机适用场景Cacheable先查缓存未命中才执行方法并缓存返回值查询接口CachePut方法执行后把返回值写入缓存新增或更新后需要立即刷新单条缓存CacheEvict方法执行后删除指定缓存删除、修改、状态变更后清理旧缓存当前项目的套餐查询已经使用 Cacheable但后台套餐新增、修改、删除和状态变更代码中暂未看到对应的缓存清理注解。这意味着如果套餐数据发生变化setmealCache 的失效策略需要在后续功能中补齐否则已缓存的套餐列表可能继续返回旧结果。六、手动 RedisTemplate 和 Spring Cache 怎么选择菜品列表选择手动操作 RedisTemplate是因为 key 前缀、按分类清理和 dish_* 批量失效规则都写得很明确开发者可以完整控制每一步。套餐列表选择 Cacheable查询规则单一注解能省去显式 get、set 的样板代码。两种方式的共同点是缓存 key 必须能区分不同查询条件写操作必须考虑旧缓存的失效。可以按下面的思路判断查询逻辑简单、键规则稳定优先使用 Spring Cache 注解。需要组合 key、按模式删除、设置特殊过期策略或实现复杂缓存流程使用 RedisTemplate 更合适。无论使用哪种方式都要先明确“数据在什么操作后变化、哪些 key 会受影响”。七、如何验证缓存是否生效可以按接口链路验证而不依赖截图清理某个 dish_分类 id key调用用户端菜品列表接口确认请求会走 Service 查询并写入缓存。再次用相同分类 id 调用接口预期直接命中 Redis不再执行菜品和口味的查询链路。在后台新增、修改、删除或停售该分类菜品后检查对应 dish key 是否被删除。再次查询用户端列表预期缓存未命中、回源数据库并写入新数据。调用用户端套餐列表两次观察第二次是否绕过套餐查询随后修改套餐数据时重点检查 setmealCache 是否有失效处理。如果用户端仍显示旧菜品信息排查顺序可以先看 Redis 服务是否连接正常再检查 dish_* 是否残留最后核对后台写操作是否走到了 cleanCache。总结day07 的核心是把“查询先读缓存、未命中再查询数据库、数据变化后删除缓存”落到实际接口中。菜品列表通过 RedisTemplate 手动控制缓存读写与失效套餐列表通过 Spring Cache 注解压缩查询缓存代码。缓存实现的重点不只在写入 Redis更在于把 key 设计和数据变更后的清理范围一起设计清楚。
延伸阅读

更多相关文章

2026/11/5 16:03:28

工程应用中的积分公式速查手册 —— 从理论到实践的桥梁

1. 积分公式在工程实践中的核心价值 第一次接触积分公式时,我和大多数工科生一样困惑:这些抽象符号和实际工程有什么关系?直到参与卫星轨道计算项目才发现,从简单的弹簧振动到复杂的电磁场分析,积分公式就像工程师的&q…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 13:55:19

GD32E10x官方例程入门:从GPIO点灯到串口调试

简介:这份GD32E10x系列官方例程面向嵌入式硬件与单片机开发者,基于ARM Cortex-M3内核,汇集兆易创新GD32E10x的示例代码,适合工业控制、消费电子、通信设备等场景的学习、验证与二次开发。压缩包共935个文件,约11.16MB&…

2026/11/5 13:50:18

基于Django的运维平台实战:资产、发布、巡检、告警一体化

简介:这份资源是一套基于Django框架开发的运维平台源码,面向具备Python基础、希望了解Web运维系统实现思路的开发者与运维人员,可用于学习主机管理、服务监控、日志查看、任务调度、权限控制、资源统计、脚本执行与故障告警等常见运维模块的落…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑