Caffeine Cache, EhCache

솔트커피·2022년 7월 19일
1

출처 : https://wave1994.tistory.com/182

Caffein

Caffeine Github를 확인해보면, "고성능의 최적의 캐싱 라이브러리" 라는 문구로 Caffeine을 설명합니다.

흔히 사용하는 Ehcache부터 Guava, ConcurrentHashMap까지의 다양한 캐시들과 비교한 내용을 담았습니다. 같이 살펴보도록 하겠습니다.

  • 캐시는 evict 로직이 Auto 로 동작하게끔 구성
  • Caffeine Cache 는 eviction policy 로 Window TinyLfu 사용

Caffein 기능

Popultation Strategy

캐시 추가 전략

Caffein Cache는 아래의 세가지 타입의 캐시로 생성하여 사용할 수 있습니다.

  1. Manual

    Cache<K, V> cache = Caffeine.newBuilder().build()

엔트리를 자동 로드하는 캐시를 생성합니다.

  1. Loading (Synchronously)

    LoadingCache<K, V> cache = Caffeine.newBuilder().build(CacheLoader<> loader)

동기 방식으로 loader를 통해 캐시를 생성합니다.

  1. Loading (Asynchronously)

    AsyncLoadingCache<K, V> cache = Caffeine.newBuilder().buildAsync( ... );]

비동기 방식으로 loader를 통해 캐시를 생성합니다.

Eviction

Caffein Cache는 아래의 세가지 타입으로 캐시를 Evict할 수 있습니다.

위에서 다뤘다시피, Caffein은 Window TinyLfu를 사용합니다.
히트율이 높고 메모리 설치 공간이 적기 때문인데요.
해당 링크를 보면 Window TinyLfu가 얼마나 효율적인지 알 수 있습니다.

  1. Sized-based

    Caffeine.newBuilder().maximumSize(long)

크기 기준으로 캐시를 제거하는 방식은 개발자가 설정한 특정 값을 기준으로 entries의 크기가 그 값을 넘을 때 entries의 일부분을 제거합니다.

위에서 언습했던 Window TinyLfu를 적용하여 가장 최근에 사용되지 않았거나, 자주 사용되어지지 않은 데이터를 제거합니다.

  1. Time-based

    Caffeine.newBuilder().expireAfterAccess(long)
    Caffeine.newBuilder().expireAfterWrite(long[, TimeUnit])
    Caffeine.newBuilder().expireAfter(Expiry)

  • expireAfterAccess
    • (캐시 생성 이후) 해당 값이 가장 최근에 대체되거나 마지막으로 읽은 후 특정 기간이 지나면 각 항목이 캐시에서 자동으로 제거 되도록 지정
  • expireAfterWrite
    • 캐시 생성 후 또는 가장 최근에 바뀐 후 특정 기간이 지나면 각 항목이 캐시에서 자동을 제거되도록 지정
  • expireAfter
    • 캐시가 생성되거나 마지막을 업데이트 된 후 지정된 시간 간격을 캐시를 새로 고침 합니다.
  1. Reference-based

    Caffeine.newBuilder().weakKeys().weakValues()
    Caffeine.newBuilder() .softValues()

  • Caffeine.weakKeys(), Caffeine.weakValues()

    • Week References를 사용하여 키를 저장합니다. Week References는 키에 대한 다른 강력한 참조(Strong References)가 없는 경우 가비지 수집할 수 있습니다.
      가비지 수집은 identitiy에만 의존하므로 전체 캐시가 동등성(.equals()) 대신 identity 동일성(==)을 사용하여 키를 비교 합니다.
  • Caffeine.softValues()

    • Soft References를 사용하여 키를 저장합니다. Soft References 오브젝트는 메모리 수요에 따라 least-recently-used 방식으로 가비지가 수집됩니다.
      소프트 레퍼런스를 사용하면 퍼포먼스가 영향을 받기 때문에 일반적으로 예측 가능한 최대 캐시 크기를 사용하는 것이 좋습니다. Week References와 마찬가지로 값이 equals 대신 identity(==) equality를 사용하여 비교됩니다.

Notification of Removal

제거되는 캐시 엔트리에 대해 리스너를 달아 추가적인 작업을 할 수 있습니다.

Caffeine.newBuilder()
.evictionListener((K, V, RemovalCause) -> { / ... / })
.removalListener((K, V, RemovalCause) -> { / ... / })
.build();

두 리스너의 차이는 캐시 관련 용어를 이해하면 적절히 사용할 수 있습니다.

  • Eviction : 캐시 정책에 의한 삭제
  • Invalidation : Caller에 의한 수동 삭제
  • Removal : invalidation과 eviction, 두 가지를 모두 포함

사용 예시

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();

Compute

외부 데이터(리소스)에 접근하며 캐시를 쓸 수 있습니다.

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))

Statistics

캐시 액세스 통계 정보를 제공합니다.
통계 정보를 사용하려면 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

EhCache는 Caffeine Cache보다 더 많은 기능을 제공합니다.

  • multi-level 캐시
  • distributed 캐시
  • 캐시 리스너
  • Off Heap에 캐싱된 데이터를 저장 가능

위 그림은 Ehcache 공식문서에 있는 Distributed Caching 관련 내용이며 각 애플리케이션 내에 저장되어 있는 캐시를 Terracotta라는 Hub 역할을 하는 분산 캐시 서버에 동기화 하는 과정입니다.

EhCache의 Distributed Caching에 대해 좀 더 알고싶다면 해당링크를 참고하세요.


위 그림은 EhCache 공식문서에 있는 Storage tiers hierarchy 구조입니다.

  • EhCache는 Heap 메모리 공간 이외의 데이터를 저장할 수 있는 Off Heap 기능을 지원
  • Off Heap 기능을 사용하면 GC로부터 자유로워 질 수 있다는 장점이 있음
  • 하지만, Off Heap에 저장되어 있는 데이터를 저장 및 불러올 때는 직렬화 비용이 발생

벤치마크 자료를 통한 성능 비교

아래 자료는 Caffeine cache 깃헙 위키에서 제공하는 데이터입니다.
측정 값과 단위는 아래와 같습니다.

  • Throughput: 단위 시간당 디지털 데이터 전송으로 처리하는 양
  • ops/s: operations per second (초당 작업)

읽기 100% 성능 측정

  • Caffeine Cache 가 가장 좋은 성능을 보여줌
  • 그 다음으로는 ConcurrentLinkedHashMap이 좋은 성능을 줌
  • 위에서 비교했던 EhCache 는 다소 아쉬운 성능

읽기 75% 쓰기 25% 성능 측정

  • 역시 Caffeine Cache 가 가장 좋은 성능을 보여줌
  • 그 다음으로는 ConcurrentLinkedHashMap이 좋은 성능을 줬지만 Caffein Cache와 성능 차이가 2배 정도 남
  • 위에서 비교했던 EhCache 는 다소 아쉬운 성능

쓰기 100% 성능 측정

  • 역시 Caffeine Cache 가 가장 좋은 성능을 보여줌
  • 그 다음으로는 ConcurrentLinkedHashMap이 좋은 성능을 줬지만 Caffein Cache와 성능 차이가 2배 정도 남
  • 위에서 비교했던 EhCache 는 다소 아쉬운 성능

정리하자면, Caffeine Cache 는 EhCache 처럼 다양한 기능은 제공하지는 않지만 심플하게 메모리에 데이터를 캐싱하고 불러오는 작업만 한다면 가장 뛰어난 성능을 보여준다.

0개의 댓글