解析Java商城结合Caffeine和Redis实现热点数据缓存方案

3 阅读3分钟

在Java商城项目中,结合Caffeine和Redis实现热点数据缓存,可以通过多级缓存架构和访问频率统计机制来实现。以下是具体实现方案:

核心思路

  1. 两级缓存结构:Caffeine(本地缓存) + Redis(分布式缓存)
  2. 热点识别:利用Caffeine的访问统计功能追踪高频访问数据
  3. 数据同步:将识别出的热点数据主动推送到Redis

实现步骤

1. 依赖配置

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.8</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

2. 配置带统计的Caffeine缓存

@Configuration
public class CaffeineConfig {

    @Bean
    public Cache<String, Product> productCache() {
        return Caffeine.newBuilder()
                .maximumSize(1000)  // 本地缓存最大容量
                .recordStats()      // 启用统计功能
                .expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期时间
                .build();
    }
}

3. 热点数据识别与缓存服务

@Service
public class ProductCacheService {

    private final Cache<String, Product> caffeineCache;
    private final RedisTemplate<String, Product> redisTemplate;
    private final ProductMapper productMapper; // 数据库访问

    // 热点阈值配置(如:5分钟内访问50次)
    private static final int HOT_THRESHOLD = 50;
    private static final Duration HOT_PERIOD = Duration.ofMinutes(5);

    @Scheduled(fixedRate = 5 * 60 * 1000) // 每5分钟检测一次
    public void detectAndCacheHotProducts() {
        Map<String, Product> cachedProducts = caffeineCache.asMap();
        long currentTime = System.currentTimeMillis();

        cachedProducts.forEach((productId, product) -> {
            // 获取该商品在统计窗口内的访问次数
            long hitCount = caffeineCache.stats().hitCount();
            
            if (hitCount >= HOT_THRESHOLD) {
                // 标记为热点并推送到Redis
                product.setHot(true);
                redisTemplate.opsForValue().set(
                    "hot:product:" + productId, 
                    product,
                    30, TimeUnit.MINUTES  // 设置较长TTL
                );
                log.info("检测到热点商品 {},访问次数: {}", productId, hitCount);
            }
        });
    }

    public Product getProduct(String productId) {
        // 1. 先查本地缓存
        Product product = caffeineCache.getIfPresent(productId);
        if (product != null) return product;

        // 2. 查询Redis热点缓存
        product = redisTemplate.opsForValue().get("hot:product:" + productId);
        if (product != null) {
            caffeineCache.put(productId, product); // 回填本地缓存
            return product;
        }

        // 3. 查询数据库
        product = productMapper.selectById(productId);
        if (product != null) {
            // 存入本地缓存(所有商品)
            caffeineCache.put(productId, product);
            
            // 异步检查是否热点
            if (isPotentialHot(productId)) {
                redisTemplate.opsForValue().set(
                    "hot:product:" + productId, 
                    product,
                    10, TimeUnit.MINUTES // 初始设置较短TTL
                );
            }
        }
        return product;
    }

    private boolean isPotentialHot(String productId) {
        // 实现简单预测逻辑(例如:新品/促销商品)
        return productId.startsWith("NEW_") || productId.endsWith("_SALE");
    }
}

4. 优化点

  1. 分级阈值策略
// 多级热点判定
if (hitCount > 100) { // 超级热点
    redisTemplate.expire("hot:product:"+productId, 2, TimeUnit.HOURS);
} else if (hitCount > 50) { // 普通热点
    redisTemplate.expire("hot:product:"+productId, 30, TimeUnit.MINUTES);
}
  1. 分布式协调(多实例场景):
// 使用Redis发布订阅同步热点信息
redisTemplate.convertAndSend("hot-product-channel", productId);

// 监听器
@RedisListener(channel = "hot-product-channel")
public void onHotProduct(String productId) {
    if (!caffeineCache.asMap().containsKey(productId)) {
        Product p = productMapper.selectById(productId);
        caffeineCache.put(productId, p);
    }
}
  1. 淘汰机制
// 当Caffeine缓存淘汰时回调
caffeineCache.policy().eviction().ifPresent(eviction -> {
    eviction.addListener((key, value, cause) -> {
        if (cause == RemovalCause.SIZE) { // 因空间不足被淘汰
            redisTemplate.delete("hot:product:" + key);
        }
    });
});

架构优势

  1. 性能优先:99%的请求由本地缓存响应(Caffeine读写性能在微秒级)
  2. 动态感知:通过Caffeine的recordStats实现实时访问追踪
  3. 资源优化
    • 本地缓存:高频访问数据
    • Redis缓存:真正热点数据
    • 数据库:长尾数据
  4. 平滑降级:Redis故障时仍可依靠本地缓存提供服务

监控指标

// 暴露缓存统计信息
public CacheStats getCacheStats() {
    return caffeineCache.stats();
}

// 典型监控指标:
// - hitRate(): 缓存命中率
// - evictionCount(): 淘汰数量
// - averageLoadPenalty(): 平均加载时间

注意事项

  1. 缓存一致性
    • 商品更新时需同步清理两级缓存
    • 使用@CacheEvict或消息队列保证一致性
  2. 内存控制
    • 本地缓存使用maximumWeight防止OOM
    • Redis设置合理的TTL避免数据膨胀
  3. 冷启动问题
    • 预加载潜在热点商品(如促销商品)
    • 采用布隆过滤器减少缓存穿透

通过这种设计,系统能够自动识别热点商品(如秒杀商品、爆款商品),并在多级缓存体系中进行高效管理,显著提升高并发场景下的性能表现。