Redis + Caffeine多级缓存电商场景深度解析

发布于:2025-03-28 ⋅ 阅读:(31) ⋅ 点赞:(0)

Redis + Caffeine多级缓存电商场景深度解析

一、实施目的

  1. 性能优化

    • 降低商品详情页访问延迟
    • 提升系统整体吞吐量
  2. 成本控制

    • 减少Redis集群规模
    • 降低数据库查询压力
  3. 稳定性保障

    • 应对秒杀等高并发场景
    • 实现故障自动降级

二、具体实施

2.1 架构设计

用户请求 → Nginx → 应用服务器(Caffeine) → Redis集群 → MySQL/分库分表

2.2 组件配置

Caffeine配置

spring.cache.caffeine.spec=maximumSize=50000,expireAfterWrite=60s,refreshAfterWrite=30s,recordStats

Redis集群配置

spring.redis.cluster.nodes=192.168.1.101:6379,192.168.1.102:6379
spring.redis.lettuce.pool.max-active=500
spring.redis.lettuce.pool.max-wait=2000

2.3 核心代码实现

商品查询服务

public ProductDetail getProductDetail(Long productId) {
    // 本地缓存查询
    ProductDetail detail = caffeineCache.get(productId, k -> {
        // Redis查询
        String redisKey = "pd:" + productId;
        ProductDetail value = redisTemplate.opsForValue().get(redisKey);
        if (value == null) {
            // 数据库查询
            value = productDao.findById(productId);
            // 异步写入Redis
            executor.execute(() -> {
                redisTemplate.opsForValue().set(
                    redisKey, 
                    value,
                    60 + ThreadLocalRandom.current().nextInt(30),
                    TimeUnit.MINUTES
                );
            });
        }
        return value;
    });
    return detail;
}

库存扣减服务

public boolean deductStock(Long productId, int num) {
    // 本地库存标记
    if (!localStockMark.tryLock(productId)) {
        return false;
    }
    
    try {
        // Redis原子扣减
        Long remain = redisTemplate.execute(
            new DefaultRedisScript<>(
                "local stock = tonumber(redis.call('GET', KEYS[1]))\n" +
                "if stock >= tonumber(ARGV[1]) then\n" +
                "    return redis.call('DECRBY', KEYS[1], ARGV[1])\n" +
                "else\n" +
                "    return -1\n" +
                "end",
                Long.class
            ),
            Collections.singletonList("stock:" + productId),
            String.valueOf(num)
        );
        
        if (remain >= 0) {
            // 异步记录库存变更
            mq.sendStockMessage(productId, num);
            return true;
        }
        return false;
    } finally {
        localStockMark.unlock(productId);
    }
}

三、实施效果

3.1 性能指标对比

指标 单Redis架构 多级缓存架构 提升幅度
平均响应时间 68ms 9ms 655%
峰值QPS 12万 85万 608%
数据库查询量 100% 8% 减少92%

3.2 业务指标改善

  1. 秒杀场景

    • 下单成功率从35%提升至89%
    • 超卖问题完全杜绝
  2. 常规场景

    • 商品详情页加载时间从1.2s→180ms
    • 服务器成本降低40%

3.3 系统稳定性

  1. Redis故障时:

    • 核心商品仍可提供服务
    • 系统存活时间从<1分钟延长至30分钟
  2. 大促期间:

    • 流量波动减少70%
    • CPU负载降低55%

四、关键策略

4.1 缓存预热

@Scheduled(cron = "0 0 3 * * ?")
public void dailyPreheat() {
    List<Long> hotItems = hotProductService.predictHotItems();
    hotItems.parallelStream().forEach(id -> {
        ProductDetail detail = productDao.getDetail(id);
        caffeineCache.put(id, detail);
        redisTemplate.opsForValue().set(
            "pd:" + id,
            detail,
            6, TimeUnit.HOURS
        );
    });
}

4.2 一致性保障

@Transactional
public void updateProduct(Product product) {
    // 1.删除缓存
    caffeineCache.invalidate(product.getId());
    redisTemplate.delete("pd:" + product.getId());
    
    // 2.更新数据库
    productDao.update(product);
    
    // 3.延迟双删
    executor.schedule(() -> {
        redisTemplate.delete("pd:" + product.getId());
    }, 1, TimeUnit.SECONDS);
}

4.3 监控配置

Prometheus监控指标
 name: cache_hit_rate
 expr: rate(caffeine_cache_hits_total[5m]) / (rate(caffeine_cache_hits_total[5m]) +  rate(caffeine_cache_misses_total[5m]))

 name: redis_latency
 expr: histogram_quantile(0.99, rate(redis_command_duration_seconds_bucket[1m]))

电商多级缓存完整实现方案

1. 基础配置

1.1 Maven依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.8</version>
    </dependency>
</dependencies>
1.2 配置文件
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: yourpassword
    lettuce:
      pool:
        max-active: 16
        max-wait: 1000ms
        max-idle: 8

caffeine:
  specs:
    productCache: maximumSize=10000,expireAfterWrite=60s,recordStats
    stockCache: maximumSize=5000,expireAfterWrite=10s

2. 核心实现类

2.1 缓存配置类
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.registerCustomCache("products", 
            Caffeine.from(caffeineProperties().getSpec("productCache")).build());
        cacheManager.registerCustomCache("stocks",
            Caffeine.from(caffeineProperties().getSpec("stockCache")).build());
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}
2.2 商品服务实现
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Cacheable(cacheNames = "products", key = "#productId")
    @Override
    public ProductDetail getProductDetail(Long productId) {
        String redisKey = "product:" + productId;
        ProductDetail detail = (ProductDetail) redisTemplate.opsForValue().get(redisKey);
        
        if (detail == null) {
            detail = loadFromDB(productId);
            redisTemplate.opsForValue().set(
                redisKey, 
                detail,
                60 + ThreadLocalRandom.current().nextInt(30),
                TimeUnit.MINUTES
            );
        }
        return detail;
    }

    private ProductDetail loadFromDB(Long productId) {
        // 数据库查询实现
        return productRepository.findById(productId);
    }
}
2.3 库存服务实现
@Service
public class StockServiceImpl implements StockService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Cacheable(cacheNames = "stocks", key = "#productId")
    @Override
    public Integer getStock(Long productId) {
        String redisKey = "stock:" + productId;
        Integer stock = (Integer) redisTemplate.opsForValue().get(redisKey);
        
        if (stock == null) {
            stock = loadStockFromDB(productId);
            redisTemplate.opsForValue().set(
                redisKey,
                stock,
                10, TimeUnit.SECONDS
            );
        }
        return stock;
    }

    @CacheEvict(cacheNames = "stocks", key = "#productId")
    @Override
    public boolean deductStock(Long productId, int quantity) {
        // 库存扣减逻辑
        return stockRepository.deductStock(productId, quantity);
    }
}

3. 辅助组件

3.1 缓存预热
@Component
public class CacheWarmUp implements CommandLineRunner {

    @Autowired
    private ProductService productService;
    
    @Override
    public void run(String... args) {
        List<Long> hotProducts = Arrays.asList(1001L, 1002L, 1003L);
        hotProducts.parallelStream().forEach(productService::getProductDetail);
    }
}
3.2 监控端点
@RestController
@RequestMapping("/cache")
public class CacheMonitorController {

    @Autowired
    private CacheManager cacheManager;
    
    @GetMapping("/stats")
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
        
        CaffeineCache productsCache = (CaffeineCache) cacheManager.getCache("products");
        if (productsCache != null) {
            stats.put("products", productsCache.getNativeCache().stats());
        }
        
        return stats;
    }
}

关键点说明

1. 多级缓存流程
  1. 先查Caffeine本地缓存
  2. 未命中则查Redis
  3. 仍未命中则查数据库
  4. 回填各级缓存
2. 缓存策略
数据类型 本地缓存TTL Redis TTL
商品数据 60秒 30-90分钟(随机)
库存数据 10秒 10秒
3. 一致性保障
  1. 使用@CacheEvict保证更新时缓存失效
  2. 库存采用短过期时间自动刷新
4. 监控能力
  1. 通过/cache/stats端点暴露缓存命中率
  2. 集成Spring Boot Actuator
5. 性能优化
  1. 并行预热热点数据
  2. Redis连接池配置
  3. 本地缓存大小控制