分布式缓存雪崩解决方案一、缓存雪崩概述缓存雪崩是指大量缓存同时失效导致大量请求直接冲击数据库造成系统性能急剧下降甚至宕机。1.1 雪崩场景┌─────────────────────────────────────────────────────────────┐ │ 正常流量 │ │ 请求 ──▶ 缓存命中 ──▶ 返回结果 │ ├─────────────────────────────────────────────────────────────┤ │ 缓存雪崩 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 ──▶ 数据库压力过大 ──▶ 宕机 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 │ └─────────────────────────────────────────────────────────────┘1.2 雪崩原因原因说明批量失效大量缓存同时过期缓存宕机Redis集群故障热点key失效热点数据缓存过期穿透攻击请求大量不存在的数据二、解决方案2.1 缓存预热Service public class CacheWarmupService { Autowired private RedisTemplateString, Object redisTemplate; Autowired private ProductRepository productRepository; PostConstruct public void warmup() { ListProduct products productRepository.findAll(); for (Product product : products) { String key product: product.getId(); redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); } } }2.2 随机过期时间public void setCacheWithRandomTTL(String key, Object value, int baseMinutes) { Random random new Random(); int extraMinutes random.nextInt(10); // 0-10分钟随机 redisTemplate.opsForValue().set(key, value, baseMinutes extraMinutes, TimeUnit.MINUTES); }2.3 多级缓存Service public class MultiLevelCacheService { Autowired private CaffeineCache localCache; Autowired private RedisTemplateString, Object redisTemplate; Autowired private ProductRepository productRepository; public Product getProduct(Long id) { String key product: id; // 一级缓存本地缓存 Object localValue localCache.getIfPresent(key); if (localValue ! null) { return (Product) localValue; } // 二级缓存Redis Object redisValue redisTemplate.opsForValue().get(key); if (redisValue ! null) { Product product (Product) redisValue; localCache.put(key, product); return product; } // 三级缓存数据库 Product product productRepository.findById(id).orElse(null); if (product ! null) { redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); localCache.put(key, product); } return product; } }三、降级策略3.1 服务降级Service public class ProductService { Autowired private CacheService cacheService; Autowired private ProductRepository productRepository; HystrixCommand(fallbackMethod getProductFallback) public Product getProduct(Long id) { Product product cacheService.getProduct(id); if (product null) { throw new RuntimeException(Product not found); } return product; } public Product getProductFallback(Long id) { // 返回降级数据 return new Product(id, Default Product, BigDecimal.ZERO); } }3.2 限流熔断Configuration public class Resilience4jConfig { Bean public CircuitBreaker circuitBreaker() { return CircuitBreaker.of(productService, CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(10)) .slidingWindowSize(100) .build()); } Bean public RateLimiter rateLimiter() { return RateLimiter.of(productService, RateLimiterConfig.custom() .limitForPeriod(100) .limitRefreshPeriod(Duration.ofSeconds(1)) .build()); } }四、热点数据处理4.1 热点key缓存Service public class HotKeyService { private SetString hotKeys Set.of( product:1001, product:1002, product:1003 ); public void handleHotKey(String key) { if (hotKeys.contains(key)) { // 热点key使用更长的过期时间 redisTemplate.expire(key, 2, TimeUnit.HOURS); } } }4.2 分布式锁更新public Product getProductWithLock(Long id) { String key product: id; String lockKey lock:product: id; Object cached redisTemplate.opsForValue().get(key); if (cached ! null) { return (Product) cached; } // 尝试获取分布式锁 Boolean lockAcquired redisTemplate.opsForValue().setIfAbsent(lockKey, locked, 5, TimeUnit.SECONDS); if (Boolean.TRUE.equals(lockAcquired)) { try { // 查询数据库 Product product productRepository.findById(id).orElse(null); if (product ! null) { redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); } return product; } finally { redisTemplate.delete(lockKey); } } else { // 等待其他线程更新缓存 Thread.sleep(100); return getProductWithLock(id); } }五、缓存宕机应对5.1 集群高可用# Redis集群配置 redis: cluster: nodes: - redis-0:6379 - redis-1:6379 - redis-2:6379 max-redirects: 35.2 服务熔断public Product getProductWithFallback(Long id) { try { return cacheService.getProduct(id); } catch (RedisConnectionFailureException e) { // Redis宕机直接查询数据库 return productRepository.findById(id).orElse(null); } }六、监控告警6.1 缓存命中率监控public class CacheMetrics { private static final Counter cacheHits Counter.build() .name(cache_hits_total) .help(Total cache hits) .register(); private static final Counter cacheMisses Counter.build() .name(cache_misses_total) .help(Total cache misses) .register(); public static void recordHit() { cacheHits.inc(); } public static void recordMiss() { cacheMisses.inc(); } }6.2 告警规则groups: - name: cache.rules rules: - alert: CacheMissRateHigh expr: sum(cache_misses_total) / (sum(cache_hits_total) sum(cache_misses_total)) 0.5 for: 5m labels: severity: warning annotations: summary: High cache miss rate description: Cache miss rate is {{ $value }}%七、总结缓存雪崩解决方案缓存预热提前加载热点数据随机过期避免同时失效多级缓存本地远程缓存服务降级熔断限流保护分布式锁避免缓存击穿监控告警及时发现问题通过综合运用这些策略可以有效预防和应对缓存雪崩问题。
分布式缓存雪崩解决方案
分布式缓存雪崩解决方案一、缓存雪崩概述缓存雪崩是指大量缓存同时失效导致大量请求直接冲击数据库造成系统性能急剧下降甚至宕机。1.1 雪崩场景┌─────────────────────────────────────────────────────────────┐ │ 正常流量 │ │ 请求 ──▶ 缓存命中 ──▶ 返回结果 │ ├─────────────────────────────────────────────────────────────┤ │ 缓存雪崩 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 ──▶ 数据库压力过大 ──▶ 宕机 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 │ │ 请求 ──▶ 缓存失效 ──▶ 数据库 │ └─────────────────────────────────────────────────────────────┘1.2 雪崩原因原因说明批量失效大量缓存同时过期缓存宕机Redis集群故障热点key失效热点数据缓存过期穿透攻击请求大量不存在的数据二、解决方案2.1 缓存预热Service public class CacheWarmupService { Autowired private RedisTemplateString, Object redisTemplate; Autowired private ProductRepository productRepository; PostConstruct public void warmup() { ListProduct products productRepository.findAll(); for (Product product : products) { String key product: product.getId(); redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); } } }2.2 随机过期时间public void setCacheWithRandomTTL(String key, Object value, int baseMinutes) { Random random new Random(); int extraMinutes random.nextInt(10); // 0-10分钟随机 redisTemplate.opsForValue().set(key, value, baseMinutes extraMinutes, TimeUnit.MINUTES); }2.3 多级缓存Service public class MultiLevelCacheService { Autowired private CaffeineCache localCache; Autowired private RedisTemplateString, Object redisTemplate; Autowired private ProductRepository productRepository; public Product getProduct(Long id) { String key product: id; // 一级缓存本地缓存 Object localValue localCache.getIfPresent(key); if (localValue ! null) { return (Product) localValue; } // 二级缓存Redis Object redisValue redisTemplate.opsForValue().get(key); if (redisValue ! null) { Product product (Product) redisValue; localCache.put(key, product); return product; } // 三级缓存数据库 Product product productRepository.findById(id).orElse(null); if (product ! null) { redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); localCache.put(key, product); } return product; } }三、降级策略3.1 服务降级Service public class ProductService { Autowired private CacheService cacheService; Autowired private ProductRepository productRepository; HystrixCommand(fallbackMethod getProductFallback) public Product getProduct(Long id) { Product product cacheService.getProduct(id); if (product null) { throw new RuntimeException(Product not found); } return product; } public Product getProductFallback(Long id) { // 返回降级数据 return new Product(id, Default Product, BigDecimal.ZERO); } }3.2 限流熔断Configuration public class Resilience4jConfig { Bean public CircuitBreaker circuitBreaker() { return CircuitBreaker.of(productService, CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(10)) .slidingWindowSize(100) .build()); } Bean public RateLimiter rateLimiter() { return RateLimiter.of(productService, RateLimiterConfig.custom() .limitForPeriod(100) .limitRefreshPeriod(Duration.ofSeconds(1)) .build()); } }四、热点数据处理4.1 热点key缓存Service public class HotKeyService { private SetString hotKeys Set.of( product:1001, product:1002, product:1003 ); public void handleHotKey(String key) { if (hotKeys.contains(key)) { // 热点key使用更长的过期时间 redisTemplate.expire(key, 2, TimeUnit.HOURS); } } }4.2 分布式锁更新public Product getProductWithLock(Long id) { String key product: id; String lockKey lock:product: id; Object cached redisTemplate.opsForValue().get(key); if (cached ! null) { return (Product) cached; } // 尝试获取分布式锁 Boolean lockAcquired redisTemplate.opsForValue().setIfAbsent(lockKey, locked, 5, TimeUnit.SECONDS); if (Boolean.TRUE.equals(lockAcquired)) { try { // 查询数据库 Product product productRepository.findById(id).orElse(null); if (product ! null) { redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); } return product; } finally { redisTemplate.delete(lockKey); } } else { // 等待其他线程更新缓存 Thread.sleep(100); return getProductWithLock(id); } }五、缓存宕机应对5.1 集群高可用# Redis集群配置 redis: cluster: nodes: - redis-0:6379 - redis-1:6379 - redis-2:6379 max-redirects: 35.2 服务熔断public Product getProductWithFallback(Long id) { try { return cacheService.getProduct(id); } catch (RedisConnectionFailureException e) { // Redis宕机直接查询数据库 return productRepository.findById(id).orElse(null); } }六、监控告警6.1 缓存命中率监控public class CacheMetrics { private static final Counter cacheHits Counter.build() .name(cache_hits_total) .help(Total cache hits) .register(); private static final Counter cacheMisses Counter.build() .name(cache_misses_total) .help(Total cache misses) .register(); public static void recordHit() { cacheHits.inc(); } public static void recordMiss() { cacheMisses.inc(); } }6.2 告警规则groups: - name: cache.rules rules: - alert: CacheMissRateHigh expr: sum(cache_misses_total) / (sum(cache_hits_total) sum(cache_misses_total)) 0.5 for: 5m labels: severity: warning annotations: summary: High cache miss rate description: Cache miss rate is {{ $value }}%七、总结缓存雪崩解决方案缓存预热提前加载热点数据随机过期避免同时失效多级缓存本地远程缓存服务降级熔断限流保护分布式锁避免缓存击穿监控告警及时发现问题通过综合运用这些策略可以有效预防和应对缓存雪崩问题。