Redis + Caffeine多级缓存
Redis + Caffeine多级缓存电商场景深度解析
一、实施目的
性能优化
- 降低商品详情页访问延迟
- 提升系统整体吞吐量
成本控制
- 减少Redis集群规模
- 降低数据库查询压力
稳定性保障
- 应对秒杀等高并发场景
- 实现故障自动降级
二、具体实施
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 业务指标改善
秒杀场景:
- 下单成功率从35%提升至89%
- 超卖问题完全杜绝
常规场景:
- 商品详情页加载时间从1.2s→180ms
- 服务器成本降低40%
3.3 系统稳定性
Redis故障时:
- 核心商品仍可提供服务
- 系统存活时间从<1分钟延长至30分钟
大促期间:
- 流量波动减少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. 多级缓存流程
- 先查Caffeine本地缓存
- 未命中则查Redis
- 仍未命中则查数据库
- 回填各级缓存
2. 缓存策略
数据类型 | 本地缓存TTL | Redis TTL |
---|---|---|
商品数据 | 60秒 | 30-90分钟(随机) |
库存数据 | 10秒 | 10秒 |
3. 一致性保障
- 使用
@CacheEvict
保证更新时缓存失效 - 库存采用短过期时间自动刷新
4. 监控能力
- 通过
/cache/stats
端点暴露缓存命中率 - 集成Spring Boot Actuator
5. 性能优化
- 并行预热热点数据
- Redis连接池配置
- 本地缓存大小控制