☰
Redis与Caffeine实现高并发店铺分类查询优化
2026/10/26 18:42:29 网站建设 项目流程

1. 项目背景与核心需求

"黑马点评"作为一款典型的本地生活服务类应用,店铺类型查询是其核心基础功能之一。在实际业务场景中,用户进入APP首页时就需要快速加载所有店铺分类,而分类数据的读取频率极高但变更频率较低(通常每天更新不超过3次),这种"读多写少"的特性使其成为缓存技术的理想应用场景。

我去年参与优化过一个日活50万+的餐饮平台,其店铺分类接口在未引入缓存前,MySQL数据库QPS峰值达到1200+,导致频繁出现响应超时。通过引入Redis缓存后,数据库负载直接下降至200QPS以下,接口响应时间从平均380ms降至28ms。这个案例让我深刻体会到合理使用缓存对高并发系统的必要性。

2. 技术方案选型与对比

2.1 缓存层级设计

在"查询店铺类型"这个场景下,我们采用两级缓存架构:

  1. 本地缓存:使用Caffeine(替代Guava Cache)
    • 超时时间:30秒
    • 最大容量:200条
  2. 分布式缓存:Redis Cluster
    • 数据结构:Hash类型
    • 过期时间:6小时
    • 淘汰策略:volatile-lru

重要提示:本地缓存时间必须远小于Redis缓存时间,避免集群环境下节点间数据不一致时间窗口过长。

2.2 数据结构设计对比

方案内存占用查询效率更新复杂度适用场景
String类型较高O(1)低简单KV场景
Hash类型中等O(1)中对象缓存
ZSet类型较高O(logN)高需要排序的场景
JSON序列化较高O(1)低兼容性要求高

最终选择Hash类型存储,字段设计如下:

HSET shop_type_cache 1 "美食" 2 "休闲娱乐" 3 "酒店"...

3. 核心实现细节

3.1 缓存查询流程

public List<ShopType> queryTypeList() { // 1. 查询本地缓存 List<ShopType> localCache = caffeineCache.getIfPresent("shop_type"); if (localCache != null) { return localCache; } // 2. 查询Redis缓存 String redisKey = "cache:shop_type"; List<ShopType> redisCache = redisTemplate.opsForHash().values(redisKey); if (!CollectionUtils.isEmpty(redisCache)) { // 回填本地缓存 caffeineCache.put("shop_type", redisCache); return redisCache; } // 3. 查询数据库 List<ShopType> dbData = shopTypeMapper.selectList(); if (!CollectionUtils.isEmpty(dbData)) { // 写入Redis(使用pipeline批量操作) redisTemplate.executePipelined((RedisCallback<Object>) connection -> { dbData.forEach(type -> { connection.hSet(redisKey.getBytes(), String.valueOf(type.getId()).getBytes(), type.toString().getBytes()); }); return null; }); // 设置过期时间 redisTemplate.expire(redisKey, 6, TimeUnit.HOURS); } return dbData; }

3.2 缓存更新策略

采用延迟双删策略保证缓存一致性:

  1. 先删除Redis缓存
  2. 执行数据库更新
  3. 延迟500ms后再次删除Redis
@Transactional public void updateShopType(ShopType type) { // 1. 删除Redis缓存 redisTemplate.delete("cache:shop_type"); // 2. 更新数据库 shopTypeMapper.updateById(type); // 3. 异步延迟删除 executorService.schedule(() -> { redisTemplate.delete("cache:shop_type"); }, 500, TimeUnit.MILLISECONDS); }

4. 性能优化关键点

4.1 缓存预热方案

在应用启动时通过InitializingBean接口自动加载:

@Override public void afterPropertiesSet() { CompletableFuture.runAsync(() -> { queryTypeList(); // 触发缓存加载 }, executorService); }

4.2 缓存雪崩防护

  1. Redis过期时间增加随机因子:
int expireTime = 6 * 3600 + new Random().nextInt(600); // 6小时±10分钟
  1. 使用互斥锁防止缓存击穿:
public List<ShopType> queryTypeListWithLock() { // ... 前序缓存查询逻辑相同 // 数据库查询前加锁 synchronized (this) { // 二次检查缓存 List<ShopType> recheck = redisTemplate.opsForHash().values(redisKey); if (!CollectionUtils.isEmpty(recheck)) { return recheck; } // 查询数据库 List<ShopType> dbData = shopTypeMapper.selectList(); // ... 后续缓存写入逻辑 } }

5. 监控与问题排查

5.1 慢查询日志配置

在Redis配置文件中添加:

slowlog-log-slower-than 5000 # 5毫秒 slowlog-max-len 1000

通过命令查看慢查询:

SLOWLOG GET 10

5.2 缓存命中率监控

使用Redis命令统计:

INFO stats

重点关注:

  • keyspace_hits
  • keyspace_misses

计算命中率:

命中率 = keyspace_hits / (keyspace_hits + keyspace_misses)

6. 常见问题解决方案

6.1 缓存穿透场景

现象:频繁查询不存在的店铺类型ID(如负数或超大数值)

解决方案:

  1. 布隆过滤器拦截
  2. 缓存空值(设置较短过期时间)
public ShopType queryById(Long id) { // 参数校验 if (id == null || id <= 0) { return null; } // ...正常查询逻辑 // 数据库未查询到结果时 if (shopType == null) { redisTemplate.opsForValue().set("cache:shop_type:null:" + id, "", 5, TimeUnit.MINUTES); } return shopType; }

6.2 本地缓存与Redis不一致

优化方案:

  1. 引入消息队列广播变更事件
  2. 使用Redis的Pub/Sub通知各节点清理本地缓存
@EventListener public void handleCacheEvictEvent(CacheEvictEvent event) { if ("shop_type".equals(event.getCacheName())) { caffeineCache.invalidate("shop_type"); } }

7. 压测数据对比

使用JMeter进行基准测试(单节点4C8G配置):

场景QPS平均响应时间错误率
无缓存1,200380ms2.3%
仅Redis缓存8,50035ms0%
两级缓存12,00018ms0%

8. 进阶优化方向

  1. 热点数据特殊处理:
// 对Top10热门分类使用独立缓存 public static final String HOT_TYPE_PREFIX = "cache:hot_type:"; public List<ShopType> getHotTypes() { // 从专门的热点Redis集群读取 return hotRedisTemplate.opsForValue().get(HOT_TYPE_PREFIX + "top10"); }
  1. 多级缓存时间策略:

    • 普通分类:6小时
    • 新入驻分类:30分钟(高频检查)
    • 即将过期分类:提前15分钟异步刷新
  2. 智能缓存预热:

# 通过历史访问数据预测缓存加载时机 def predict_load_time(): from sklearn.linear_model import LinearRegression # 使用机器学习模型预测最佳预热时间 ...

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询