출처 : https://wave1994.tistory.com/182
Caffeine Github를 확인해보면, "고성능의 최적의 캐싱 라이브러리" 라는 문구로 Caffeine을 설명합니다.
흔히 사용하는 Ehcache부터 Guava, ConcurrentHashMap까지의 다양한 캐시들과 비교한 내용을 담았습니다. 같이 살펴보도록 하겠습니다.
캐시 추가 전략
Caffein Cache는 아래의 세가지 타입의 캐시로 생성하여 사용할 수 있습니다.
Cache<K, V> cache = Caffeine.newBuilder().build()
엔트리를 자동 로드하는 캐시를 생성합니다.
LoadingCache<K, V> cache = Caffeine.newBuilder().build(CacheLoader<> loader)
동기 방식으로 loader를 통해 캐시를 생성합니다.
AsyncLoadingCache<K, V> cache = Caffeine.newBuilder().buildAsync( ... );]
비동기 방식으로 loader를 통해 캐시를 생성합니다.
Caffein Cache는 아래의 세가지 타입으로 캐시를 Evict할 수 있습니다.
위에서 다뤘다시피, Caffein은 Window TinyLfu를 사용합니다.
히트율이 높고 메모리 설치 공간이 적기 때문인데요.
해당 링크를 보면 Window TinyLfu가 얼마나 효율적인지 알 수 있습니다.
Caffeine.newBuilder().maximumSize(long)
크기 기준으로 캐시를 제거하는 방식은 개발자가 설정한 특정 값을 기준으로 entries의 크기가 그 값을 넘을 때 entries의 일부분을 제거합니다.
위에서 언습했던 Window TinyLfu를 적용하여 가장 최근에 사용되지 않았거나, 자주 사용되어지지 않은 데이터를 제거합니다.
Caffeine.newBuilder().expireAfterAccess(long)
Caffeine.newBuilder().expireAfterWrite(long[, TimeUnit])
Caffeine.newBuilder().expireAfter(Expiry)
Caffeine.newBuilder().weakKeys().weakValues()
Caffeine.newBuilder() .softValues()
Caffeine.weakKeys(), Caffeine.weakValues()
Caffeine.softValues()
제거되는 캐시 엔트리에 대해 리스너를 달아 추가적인 작업을 할 수 있습니다.
Caffeine.newBuilder()
.evictionListener((K, V, RemovalCause) -> { / ... / })
.removalListener((K, V, RemovalCause) -> { / ... / })
.build();
두 리스너의 차이는 캐시 관련 용어를 이해하면 적절히 사용할 수 있습니다.
사용 예시
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.evictionListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was evicted (%s)%n", key, cause))
.removalListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was removed (%s)%n", key, cause))
.build();
외부 데이터(리소스)에 접근하며 캐시를 쓸 수 있습니다.
graphs.asMap().compute(key, (k, v) -> { / ... / });
불러온 캐시 데이터를 Map으로 변경한 후 키를 차례로 조작할 수 있습니다.
graphs.asMap().compute(key, (k, v) -> {
Graph graph = createExpensiveGraph(key);
... // update a secondary store
return graph;
});
map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))
캐시 액세스 통계 정보를 제공합니다.
통계 정보를 사용하려면 Caffein.recirdStats()
를 설정해주면 됩니다.
Cache.stats() 메소드가 CacheStats를 반환해주는데요.
CacheStats는 아래와 같은 내용을 가집니다.
public final class CacheStats {
// ...
private final long hitCount;
private final long missCount;
private final long loadSuccessCount;
private final long loadFailureCount;
private final long totalLoadTime;
private final long evictionCount;
private final long evictionWeight;
// ...
public double hitRate() {/* ... */}
public long evictionCount() {/* ... */}
public double averageLoadPenalty() {/* ... */}
// ...
}
EhCache는 Caffeine Cache보다 더 많은 기능을 제공합니다.
위 그림은 Ehcache 공식문서에 있는 Distributed Caching 관련 내용이며 각 애플리케이션 내에 저장되어 있는 캐시를 Terracotta라는 Hub 역할을 하는 분산 캐시 서버에 동기화 하는 과정입니다.
EhCache의 Distributed Caching에 대해 좀 더 알고싶다면 해당링크를 참고하세요.
위 그림은 EhCache 공식문서에 있는 Storage tiers hierarchy 구조입니다.
아래 자료는 Caffeine cache 깃헙 위키에서 제공하는 데이터입니다.
측정 값과 단위는 아래와 같습니다.
정리하자면, Caffeine Cache 는 EhCache 처럼 다양한 기능은 제공하지는 않지만 심플하게 메모리에 데이터를 캐싱하고 불러오는 작업만 한다면 가장 뛰어난 성능을 보여준다.