分享四个关于网站缓存的技巧

12 阅读1分钟

一、热点数据预加载

// 使用Redis HyperLogLog统计访问频率  
public void recordAccess(Long productId) {  
String key = "access:product:" + productId;  
redis.pfadd(key, UUID.randomUUID().toString());  
redis.expire(key, 60); // 统计最近60秒  
}  

// 定时任务检测热点  
@Scheduled(fixedRate = 10000)  
public void detectHotKeys() {  
Set<String> keys = redis.keys("access:product:*");  
keys.forEach(key -> {  
    long count = redis.pfcount(key);  
    if (count > 1000) { // 阈值  
        Long productId = extractId(key);  
        preloadProduct(productId);  
    }  
});  
}

二、空值缓存

public Product getProduct(Long id) {  
String key = "product:" + id;  
Product product = redis.get(key);  
if (product != null) {  
    if (product.isEmpty()) { // 空对象标识  
        return null;  
    }  
    return product;  
}  

product = productDao.findById(id);  
if (product == null) {  
    redis.setex(key, 300, "empty"); // 缓存空值5分钟  
    return null;  
}  

redis.setex(key, 3600, product);  
return product;  
}

三、需要增加熔断降级

               commandProperties = {  
                   @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),  
                   @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")  
               })  
public Product getProduct(Long id) {  
    return productDao.findById(id);  
}  

public Product getProductFallback(Long id) {  
    return new Product().setDefault(); // 返回兜底数据  
}

四、延迟双删策略

public void updateProduct(Product product) {  
    // 1. 先删缓存  
    redis.delete("product:" + product.getId());  

    // 2. 更新数据库  
    productDao.update(product);  

    // 3. 延时再删  
    executor.schedule(() -> {  
        redis.delete("product:" + product.getId());  
    }, 500, TimeUnit.MILLISECONDS);  
}