发布时间:2026/7/16 8:45:34
【 苍穹外卖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/7/16 8:45:34

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

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

2026/7/16 9:45:40

TVA-具身智能:如何跨越电子与原子鸿沟(12)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

2026/7/16 9:45:40

TVA-具身智能:如何跨越电子与原子鸿沟(11)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

2026/7/16 9:45:40

TVA-具身智能:如何跨越电子与原子鸿沟(8)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

2026/7/16 9:45:40

ARM Cortex-M开发板ISP升级指南与实战

1. CF5010RBT60开发板ISP升级初探 第一次拿到微五科技的CF5010RBT60开发板时,最让我头疼的就是程序下载方式的选择。作为一款基于ARM Cortex-M内核的嵌入式开发板,它支持多种烧录方式,但ISP(In-System Programming)无疑…

2026/7/16 9:45:40

技术选型评估:从创新宣传到工程落地的理性决策框架

在技术领域,我们经常听到一些产品被冠以“改变世界”的称号。这类宣传往往伴随着高度的市场期待和技术创新承诺。作为开发者,我们需要保持理性的判断力,既要关注技术本身的突破性,也要评估其实际落地能力和工程可行性。 真正能够…

2026/7/16 9:40:39

多传感器融合定位:从松紧耦合到算法实战

1. 多传感器融合定位技术概述 想象一下你正在玩一个"盲人摸象"的游戏:闭着眼睛单靠触摸大象的鼻子,可能会误以为这是一条蛇;只摸到耳朵又觉得像扇子。这就是单一传感器的局限性——就像自动驾驶汽车仅靠GPS在隧道中会"失明&qu…

2026/7/15 17:04:06

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/15 22:21:34

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/15 23:18:22

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/16 0:04:06

AD9215BRUZRL7-105是一款工业 10bit 流水线模数转换器

型号介绍AD9215BRUZRL7-105 是一款10 位单通道高速流水线模数转换器,它采用模拟单电源独立供电 数字驱动分离供电双电源架构,模拟电源 AVDD 额定 3V,允许工作区间 2.7V~3.3V;数字驱动电源 DRV 独立设计,支持 2.25V~3.…

2026/7/16 0:04:06

LTC4418IUF#TRPBF是一款工业级双通道控制器

型号介绍LTC4418IUF#TRPBF 是一款工业级双通道电源路径控制器,它芯片采用 20 引脚 4mm4mm QFN 封装,底部附带外露散热焊盘作为 21 号 GND 引脚,热阻 θJA 为 47℃/W、θJC 仅 4.5℃/W,散热性能适配大功率切换场景;温区…

2026/7/16 0:04:06

uos-exporter核心组件解析:10个关键监控导出器功能详解

uos-exporter核心组件解析:10个关键监控导出器功能详解 【免费下载链接】uos-exporter uos-exporter collects metrics from os 项目地址: https://gitcode.com/openeuler/uos-exporter 前往项目官网免费下载:https://ar.openeuler.org/ar/ uos-…

2026/7/15 13:36:32

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…