当前位置: 首页 > news >正文

Spring Cache + Redis 缓存套餐数据,我是怎么在苍穹外卖项目里用起来的?

Spring Cache + Redis 在苍穹外卖项目中的实战应用

1. 为什么选择Spring Cache与Redis组合

在开发苍穹外卖这样的高并发餐饮系统时,数据库查询压力往往成为性能瓶颈。特别是套餐数据这类变化频率较低但访问量极高的内容,每次请求都直接查询数据库显然不是最优解。

Spring Cache作为Spring生态的缓存抽象层,提供了一套简洁的注解驱动缓存方案。而Redis作为内存数据库,其极高的读写性能使其成为缓存后端的理想选择。两者的结合能够:

  • 减少数据库访问次数,降低系统负载
  • 提升响应速度,改善用户体验
  • 通过注解简化开发,提高代码可维护性

典型应用场景

  • 菜单/套餐展示(高频读取)
  • 用户信息缓存(减少重复查询)
  • 店铺营业状态(快速访问)

2. 项目集成实战

2.1 基础环境配置

首先确保项目中已包含必要依赖:

<!-- Redis Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Spring Cache Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>

在application.yml中配置Redis连接:

spring: redis: host: 127.0.0.1 port: 6379 password: database: 0 lettuce: pool: max-active: 8 max-wait: -1ms max-idle: 8 min-idle: 0

启动类添加@EnableCaching注解激活缓存功能:

@SpringBootApplication @EnableCaching public class SkyApplication { public static void main(String[] args) { SpringApplication.run(SkyApplication.class, args); } }

2.2 缓存套餐数据实战

假设我们有套餐服务MealService,以下是典型缓存应用:

@Service public class MealServiceImpl implements MealService { @Autowired private MealMapper mealMapper; @Cacheable(value = "mealCache", key = "#categoryId") public List<Meal> getMealsByCategory(Long categoryId) { // 实际数据库查询 return mealMapper.findByCategoryId(categoryId); } @CachePut(value = "mealCache", key = "#meal.categoryId") public Meal addMeal(Meal meal) { mealMapper.insert(meal); return meal; } @CacheEvict(value = "mealCache", key = "#categoryId") public void deleteMeal(Long id, Long categoryId) { mealMapper.deleteById(id); } }

关键注解解析

注解作用适用场景
@Cacheable方法执行前检查缓存,存在则直接返回查询操作
@CachePut总是执行方法,并将结果存入缓存新增/更新操作
@CacheEvict清除指定缓存删除操作

2.3 缓存Key设计策略

合理的Key设计对缓存效率至关重要。苍穹外卖项目中我们采用以下策略:

  1. 业务前缀:如mealCache::
  2. 参数组合:对于多参数方法,使用SpEL表达式组合
    @Cacheable(value = "mealCache", key = "#shopId+':'+#categoryId")
  3. 自定义Key生成器(复杂场景):
    @Configuration public class CacheConfig { @Bean public KeyGenerator mealKeyGenerator() { return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb.append("meal_"); sb.append(params[0]); return sb.toString(); }; } }

3. 性能优化与问题解决

3.1 缓存穿透防护

当查询不存在的数据时,可能导致大量请求直接穿透到数据库。解决方案:

  1. 空值缓存
    @Cacheable(value = "mealCache", key = "#id", unless = "#result == null") public Meal getById(Long id) { Meal meal = mealMapper.selectById(id); if(meal == null) { // 缓存空对象,设置较短过期时间 return new NullMeal(); } return meal; }
  2. 布隆过滤器:在缓存层前增加过滤

3.2 缓存雪崩预防

大量缓存同时失效导致数据库压力骤增:

  1. 差异化过期时间
    @Configuration public class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) .computePrefixWith(name -> name + "::"); return RedisCacheManager.builder(factory) .cacheDefaults(config) .withInitialCacheConfigurations(Map.of( "mealCache", config.entryTtl(Duration.ofHours(1)), "userCache", config.entryTtl(Duration.ofDays(1)) )) .build(); } }
  2. 热点数据永不过期:配合异步刷新

3.3 缓存一致性保障

数据库与缓存数据不一致的解决方案:

  1. 双写模式
    @Transactional public void updateMeal(Meal meal) { mealMapper.updateById(meal); redisTemplate.opsForValue().set( "meal::"+meal.getId(), meal, Duration.ofHours(1) ); }
  2. 失效模式(更推荐):
    @Transactional @CacheEvict(value = "mealCache", key = "#meal.id") public void updateMeal(Meal meal) { mealMapper.updateById(meal); }

4. 高级应用技巧

4.1 条件化缓存

通过conditionunless参数实现条件缓存:

@Cacheable(value = "mealCache", key = "#id", condition = "#id != null", unless = "#result.price > 100") public Meal getMealById(Long id) { return mealMapper.selectById(id); }

4.2 多级缓存策略

结合本地缓存与Redis实现多级缓存:

  1. Caffeine本地缓存配置
    @Configuration public class CacheConfig { @Bean public CaffeineCacheManager caffeineCacheManager() { Caffeine<Object, Object> caffeine = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES); return new CaffeineCacheManager("localMealCache", "localUserCache") .setCaffeine(caffeine); } }
  2. 自定义缓存解析器实现多级缓存逻辑

4.3 缓存监控与统计

通过Redis命令或Spring Boot Actuator监控缓存命中率:

management: endpoints: web: exposure: include: health,info,caches

定制缓存统计Endpoint:

@Component public class CacheMetricsEndpoint extends AbstractEndpoint<Map<String, Object>> { @Autowired private CacheManager cacheManager; public CacheMetricsEndpoint() { super("cachemetrics"); } @Override public Map<String, Object> invoke() { Map<String, Object> metrics = new HashMap<>(); if(cacheManager instanceof RedisCacheManager) { // 获取Redis特定指标 } return metrics; } }

5. 实际项目经验分享

在苍穹外卖项目中,我们遇到了几个值得注意的情况:

  1. 套餐分类变更时的缓存更新

    @Transactional @Caching(evict = { @CacheEvict(value = "mealCache", key = "#oldCategoryId"), @CacheEvict(value = "mealCache", key = "#meal.categoryId") }) public void changeCategory(Meal meal, Long oldCategoryId) { mealMapper.updateById(meal); }
  2. 批量操作时的缓存处理

    @CacheEvict(value = "mealCache", allEntries = true) public void batchUpdate(List<Meal> meals) { mealMapper.batchUpdate(meals); }
  3. 缓存预热策略

    @PostConstruct public void preloadPopularMeals() { List<Long> popularIds = mealMapper.selectPopularIds(); popularIds.forEach(id -> { Meal meal = mealMapper.selectById(id); redisTemplate.opsForValue().set( "meal::"+id, meal, Duration.ofHours(2) ); }); }

对于特别热门的套餐数据,我们最终采用了本地缓存+Redis的双层结构,将缓存命中率从75%提升到了98%,数据库查询量减少了近90%。

http://www.jsqmd.com/news/943661/

相关文章:

  • 武汉哪家屋面虹吸排水系统公司专业且自有施工队 - 速递信息
  • 3步搞定B站视频下载:BiliDownload帮你轻松获取无水印高清资源
  • 2026年昆明新能源抓钢机选型指南:港口装卸与废钢回收降本方案对比 - 企业名录优选推荐
  • 药物筛选新手段,AI分子智算
  • 2026南京查漏水公司推荐:这家本地老牌最靠谱 - 资讯快报
  • 2026 年 6 月童书馆品牌加盟推荐 TOP5 :中小创业者低风险选项目稳拿收益 - 资讯快报
  • ParsecVDisplay:Windows虚拟显示器终极指南,零硬件成本扩展多屏工作区
  • 泰州GEO优化公司怎么选才不踩坑?行业内幕与选型标准 (2026年6月最新) - 商业新知
  • 2026年贵州新能源抓钢机选购完全指南:从废钢回收到港口装卸的全场景解决方案 - 企业名录优选推荐
  • 告别传统命令行:在VS Code中重塑你的Fortran科学计算开发体验
  • 2026专业决策咨询数据公司综合能力排行哪家好 推荐一下 - 奔跑123
  • 3分钟上手!用Mousecape轻松定制你的macOS光标主题
  • 基于图像识别的游戏压枪助手:从零配置到实战精通的完整指南
  • 3大核心突破:OmenSuperHub如何重新定义惠普游戏本性能管理
  • ESP32蓝牙串口通信实战:从环境搭建到指令控制LED
  • 2026广州奢侈品包包回收行情|新手避坑+正规上门机构实测 - 合扬奢侈品交易中心
  • [开源] 医院药品效期调拨优化系统:用生存分析+整数规划降低医院药品报废率
  • 2026年嘉兴家政公司排行榜,本土品牌谁更值得选? - 招财兔数字员工
  • 收购了一个新品牌,文档却成了“烂摊子”
  • Input Leap:一套键鼠控制多台电脑,彻底告别设备切换烦恼
  • DIY模块化并行计算集群:基于Parallella与3D打印的DEMAC项目实践
  • 高效智能的图像去重完全指南:使用AntiDupl.NET告别重复文件困扰
  • 为什么AI代码审查工具降低缺陷率总失败?先补齐这3个条件
  • 2026通化市黄金回收白银回收铂金回收店铺哪家好 实地筛选靠谱回收大全及联系方式 - 余生黄金回收
  • Arduino直流减速电机驱动与PWM调速:打造智能旋转展示台
  • 山东光储产品一站式采购及解决方案哪家好
  • KMS_VL_ALL_AIO:一劳永逸解决Windows和Office激活问题的终极方案
  • AI名片扫描与LinkedIn集成:智能人脉管理实战解析
  • Anki记忆工具:3个步骤掌握科学记忆的秘密武器
  • DIY模块合成器滑音电路:从RC积分原理到PCB制作全解析