springboot + caffeine

167 阅读4分钟

1. 添加maven依赖

<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-cache</artifactId>  
<version>2.4.2</version>  
</dependency>
<dependency>  
<groupId>com.github.ben-manes.caffeine</groupId>  
<artifactId>caffeine</artifactId>  
<version>2.9.0</version>  
</dependency>

2. 自动使用

  • 添加@EnableCaching注解和新增配置类(或者yml中配置)
@Configuration  
@EnableCaching  
public class CacheConfig {  
  
@Bean  
public CacheManager cacheManager() {  
CaffeineCacheManager cacheManager = new CaffeineCacheManager();  
Caffeine<Object, Object> objectCaffeine = Caffeine.newBuilder()  
.expireAfterWrite(29, TimeUnit.DAYS);  
cacheManager.setCaffeine(objectCaffeine);  
return cacheManager;  
}  
}
/*  
initialCapacity=[integer]: 初始的缓存空间大小  
  
maximumSize=[long]: 缓存的最大条数  
  
maximumWeight=[long]: 缓存的最大权重  
  
expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期  
  
expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期  
  
refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存  
  
weakKeys: 打开key的弱引用  
  
weakValues:打开value的弱引用  
  
softValues:打开value的软引用  
  
recordStats:开发统计功能  
*/
  • 配置文件
spring:
  cache:
    type: caffeine
    cache-names:
    - cacheName
    caffeine:
      spec: maximumSize=1024,refreshAfterWrite=10s
  • 使用spring提供的 @Cacheable@CachePut@CacheEvict等注解来方便的使用caffeine缓存
@Cacheable 触发缓存入口(这里一般放在创建和获取的方法上,@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存)

@CacheEvict 触发缓存的eviction(用于删除的方法上)

@CachePut 更新缓存且不影响方法执行(用于修改的方法上,该注解下的方法始终会被执行)

@Caching 将多个缓存组合在一个方法上(该注解可以允许一个方法同时设置多个注解)

@CacheConfig 在类级别设置一些缓存相关的共同配置(与其它缓存配合使用)
   /**
     * 要使用的cache的名字
     */
    @AliasFor("cacheNames")
    String[] value() default {};
 
    /**
     * 同value(),决定要使用那个缓存
     */
    @AliasFor("value")
    String[] cacheNames() default {};
 
    /**
     * 使用SpEL表达式来设定缓存的key,如果不设置默认方法上所有参数都会作为key的一部分
     */
    String key() default "";
 
    /**
     * 用来生成key,与key()不可以共用
     */
    String keyGenerator() default "";
 
    /**
     * 设定要使用的cacheManager,必须先设置好cacheManager的bean,这是使用该bean的名字
     */
    String cacheManager() default "";
 
    /**
     * 使用cacheResolver来设定使用的缓存,用法同cacheManager,但是与cacheManager不可以同时使用
     */
    String cacheResolver() default "";
 
    /**
     * 使用SpEL表达式设定出发缓存的条件,在方法执行前生效
     */
    String condition() default "";
 
    /**
     * 使用SpEL设置出发缓存的条件,这里是方法执行完生效,所以条件中可以有方法执行后的value
     */
    String unless() default "";
 
    /**
     * 用于同步的,在缓存失效(过期不存在等各种原因)的时候,如果多个线程同时访问被标注的方法
     * 则只允许一个线程通过去执行方法
     */
    boolean sync() default false;
  • spel

@Cacheable(value="userCache", key="#p0")

使用方法参数时我们可以直接使用“#参数名”或者“#p参数index

3. 单独使用

@Configuration  
public class CacheConfig {  
@Bean  
public Cache<String, Object> caffeineCache() {  
return Caffeine.newBuilder()  
// 设置最后一次写入或访问后经过固定时间过期  
.expireAfterWrite(29, TimeUnit.DAYS)  
// 初始的缓存空间大小  
.initialCapacity(100)  
// 缓存的最大条数  
.maximumSize(1000)  
.build();  
}  
}

public class SpringbootTest {  
  
@Resource  
Cache<String, Object> tokenCache;  
  
  
@Test  
public void test() {  
tokenCache.put("token", "ddd");  
Object token = tokenCache.get("token", key -> "default);  
System.out.println();  
}  
}

4. 算法简介

  • FIFO:先进先出,在这种淘汰算法中,先进入缓存的会先被淘汰,会导致命中率很低。

  • LRU:最近最少使用算法,每次访问数据都会将其放在我们的队尾,如果需要淘汰数据,就只需要淘汰队首即可。仍然有个问题,如果有个数据在 1 分钟访问了 1000次,再后 1 分钟没有访问这个数据,但是有其他的数据访问,就导致了我们这个热点数据被淘汰。

  • LFU:最近最少频率使用,利用额外的空间记录每个数据的使用频率,然后选出频率最低进行淘汰。这样就避免了 LRU 不能处理时间段的问题。

5. 定时驱逐策略

  • expireAfterAccess(long, TimeUnit):在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。

  • expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期。

  • expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算。

6.Caffeine使用 Window TinyLfu 回收策略

git地址github.com/ben-manes/c…