Caffeine
I. SpringBoot Cache
어플리케이션은 기능 구현을 위해 다양한 DataSource
를 사용한다. 자주 사용되는 기능일수록 특정 DataSource에 대한 접근이 늘어나고, 그에 따른 부담이 증가한다. Cache는 어플리케이션이 특정 DataSource에 접근하지 않고, 데이터가 임시로 저장된 공간에 접근해 빠르게 데이터를 반환할 수 있도록 한다. SpringBoot는 이러한 Cache를 추상화한 Annotation
을 통해 쉽게 사용할 수 있도록 지원한다.
1. QuickStart
1) Dependency
1
2
3
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-cache'
}
2) Enable Caching
1
2
3
4
5
6
7
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@EnableCaching
을 통해 Spring Cache를 활성화한다. 이를 통해 @Cacheable, @CachePut, @CacheEvict 등의 Cache 관련 Annotation을 사용할 수 있다. @EnableCaching
을 적용하는 것만으로 Cache를 사용할 수는 없고, CacheManager 구현체를 @Bean
으로 등록해야한다.
3) Config CacheManager
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableCaching
public class LocalCacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(/* ${Collection<? extends Cache> caches} */);
return simpleCacheManager;
}
}
simpleCacheManager.setCaches()
를 통해 사용하고자 하는 CacheProvider(Eh, Guava, Caffeine 등)를 지정할 수 있다. CacheProvider를 지정하지 않으면, ConcurrentMapCache
를 사용한다. ConcurrentMapCache
는 null 허용 여부, concurrentMap 지정 여부에 따라 생성자가 다르므로, 필요에 따라 직접 생성해 사용할 수도 있다.
4) @Cacheable(value="cacheName", key="cacheKey", condition="#parameter=True")
@Cacheable
은 메소드 호출 결과를 캐시에 저장한다. 또한 동일한 캐시가 존재할 경우, 저장된 데이터를 반환한다. value
, key
, condition
설정에 따라 저장 조건을 다양하게 가져갈 수 있다. 또한 SpringExpressionLanguage를 통한 동적인 핸들링도 가능하다.
- value: 캐시 이름을 지정한다.
value={"cacheName1", "cacheName2"}
와 같은 형태로 두 종류의 캐시에 한번에 저장하는 것도 가능하다. - key: 캐시 key를 지정한다.
- condition: 특정 조건을 만족할 경우에만 저장 혹은 조회되도록 한다.
5) @CachePut(value="cacheName", key="cacheKey", condition="#parameter=True")
@CachePut
은 기본적으로 @Cacheable
과 사용 방식이 동일하나, Cache에 저장된 데이터를 조회하지 않고 실행 결과를 무조건 새로 저장한다.
6) @CacheEvict(value="cacheName", allEntries="True")
Cache에 메소드 실행 결과를 저장하다보면, 자연스럽게 Cache의 데이터양이 늘어나고 이는 곧 저장 공간의 낭비와 캐시 성능 저하로 이어진다. @CacheEvict
는 이를 방지하기 위해, 호출 시 조건에 따라 저장된 캐시를 삭제한다.
II. Caffeine
Caffeine은 신뢰도가 높은 CacheProvider다. 주요 특징은 다음과 같다.
- Cache 항목 자동 로딩
- Cache Frequency, Recency에 따른 size-based-eviction
- Cache 만료에 대한 추상화 제공
1. QuickStart
1) Dependency
1
2
3
4
5
dependencies {
// Cache
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation "com.github.ben-manes.caffeine:caffeine:3.1.8"
}
2) Enable Caching
1
2
3
4
5
6
7
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4) CacheEnum
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
public enum CacheType {
TEST("test", 60 * 60 * 24 * 7, 1);
private final String cacheName;
private final int secsToExpireAfterWrite;
private final int entryMaxSize;
CacheType(String cacheName, int secsToExpireAfterWrite, int entryMaxSize) {
this.cacheName = cacheName;
this.secsToExpireAfterWrite = secsToExpireAfterWrite;
this.entryMaxSize = entryMaxSize;
}
}
사용될 Cache 형태를 Enum으로 관리함으로써, 사용될 캐시 관리를 원활하게 할 수 있다.
3) Config CacheManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
List<CaffeineCache> caches = Arrays.stream(CacheType.values())
.map(cacheType ->
new CaffeineCache(
cacheType.getCacheName(),
Caffeine.newBuilder()
.recordStats()
.expireAfterWrite(cacheType.getSecsToExpireAfterWrite(), TimeUnit.SECONDS)
.maximumSize(cacheType.getEntryMaxSize())
.build()))
.toList();
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return cacheManager;
}
}
stream을 통해 Enum에 정의된 Cache들을 등록한다.