使用Caffeine实现高效本地缓存

使用Caffeine实现高效本地缓存

1. Caffeine简介

Caffeine是一个高性能的Java缓存库,由Guava缓存的原作者Ben Manes开发。它提供了近乎最优的命中率,并且具有出色的并发性能。Caffeine的设计借鉴了Guava Cache和ConcurrentLinkedHashMap,但在性能和功能上都有显著提升。

主要特性:

  • 自动加载缓存项
  • 基于大小的淘汰策略
  • 基于时间的淘汰策略
  • 异步刷新
  • 统计信息收集
  • 事件监听

2. 为什么要使用本地缓存?

在分布式系统中,使用本地缓存可以带来以下好处:

  1. 减少远程调用:将频繁访问的数据缓存在本地,减少对远程服务或数据库的调用
  2. 提升性能:本地内存访问速度远快于网络请求
  3. 降低系统负载:减少对后端服务的压力
  4. 提高系统可用性:即使远程服务不可用,本地缓存仍可提供服务
  5. 降低成本:减少网络带宽和数据库资源的消耗

3. 本地缓存的优缺点

优点:

  • 访问速度快
  • 实现简单
  • 不依赖外部服务
  • 适合缓存热点数据

缺点:

  • 内存占用较大
  • 数据一致性难以保证
  • 不适合缓存大量数据
  • 集群环境下缓存同步困难

4. 常用缓存淘汰策略

  1. FIFO(First In First Out):先进先出,淘汰最早进入缓存的数据
  2. LRU(Least Recently Used):最近最少使用,淘汰最久未使用的数据
  3. LFU(Least Frequently Used):最少使用,淘汰使用频率最低的数据
  4. TTL(Time To Live):设置缓存项的生存时间,到期自动淘汰
  5. 基于大小的淘汰:当缓存达到最大容量时,淘汰部分数据

5. Caffeine在Spring Boot中的使用

5.1 添加依赖

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

5.2 配置缓存

@Configuration
public class CacheConfig {

    @Bean
    public Cache<String, Object> caffeineCache() {
        return Caffeine.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期
                .maximumSize(1000) // 最大缓存1000个对象
                .recordStats() // 开启统计功能
                .build();
    }
}

5.3 使用缓存

@Service
public class UserService {

    @Autowired
    private Cache<String, User> caffeineCache;

    public User getUser(String userId) {
        return caffeineCache.get(userId, key -> {
            // 缓存未命中时,从数据库加载
            return userRepository.findById(userId).orElse(null);
        });
    }
}

6. Caffeine API列表

API功能
Caffeine.newBuilder()创建缓存构建器
maximumSize(long)设置最大缓存大小
expireAfterWrite(long, TimeUnit)设置写入后过期时间
expireAfterAccess(long, TimeUnit)设置访问后过期时间
refreshAfterWrite(long, TimeUnit)设置写入后刷新时间
recordStats()开启统计功能
build()构建缓存实例
get(K, Function<? super K, ? extends V>)获取缓存值,未命中时执行加载函数
put(K, V)手动添加缓存项
invalidate(K)移除指定缓存项
invalidateAll()移除所有缓存项
asMap()将缓存转换为ConcurrentMap
stats()获取缓存统计信息

7. SpringBoot集成Caffeine+Redis实现一二级缓存

7.1 配置类

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .maximumSize(1000));
        
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(30)))
                .build();
        
        return new TwoLevelCacheManager(caffeineCacheManager, redisCacheManager);
    }
}

7.2 二级缓存管理器

public class TwoLevelCacheManager implements CacheManager {

    private final CacheManager level1CacheManager;
    private final CacheManager level2CacheManager;

    public TwoLevelCacheManager(CacheManager level1CacheManager, CacheManager level2CacheManager) {
        this.level1CacheManager = level1CacheManager;
        this.level2CacheManager = level2CacheManager;
    }

    @Override
    public Cache getCache(String name) {
        return new TwoLevelCache(level1CacheManager.getCache(name), 
                               level2CacheManager.getCache(name));
    }

    @Override
    public Collection<String> getCacheNames() {
        return level1CacheManager.getCacheNames();
    }
}

7.3 二级缓存实现

public class TwoLevelCache implements Cache {

    private final Cache level1Cache;
    private final Cache level2Cache;

    public TwoLevelCache(Cache level1Cache, Cache level2Cache) {
        this.level1Cache = level1Cache;
        this.level2Cache = level2Cache;
    }

    @Override
    public String getName() {
        return level1Cache.getName();
    }

    @Override
    public Object getNativeCache() {
        return level1Cache.getNativeCache();
    }

    @Override
    public ValueWrapper get(Object key) {
        ValueWrapper value = level1Cache.get(key);
        if (value == null) {
            value = level2Cache.get(key);
            if (value != null) {
                level1Cache.put(key, value.get());
            }
        }
        return value;
    }

    @Override
    public void put(Object key, Object value) {
        level1Cache.put(key, value);
        level2Cache.put(key, value);
    }

    @Override
    public void evict(Object key) {
        level1Cache.evict(key);
        level2Cache.evict(key);
    }

    @Override
    public void clear() {
        level1Cache.clear();
        level2Cache.clear();
    }
}

7.4 使用示例

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product getProduct(String id) {
        // 从数据库获取产品信息
        return productRepository.findById(id).orElse(null);
    }
}

总结

本文详细介绍了如何使用Caffeine实现高效的本地缓存,包括其核心特性、使用场景、在Spring Boot中的集成方法,以及如何与Redis结合实现一二级缓存。通过合理使用本地缓存,可以显著提升系统性能,降低后端服务压力。在实际项目中,建议根据具体业务场景选择合适的缓存策略,并注意缓存一致性问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值