스프링의 캐시 추상화
스프링은 캐시 매니저를 통해 캐싱 방법을 추상화한다. 즉, 특정 캐시 기술에 종속되지 않고 AOP를 통해 적용하여 애플리케이션 코드에 캐시 기능을 추가할 수 있다.
스프링의 캐시 매니저는 다음과 같다.
- ConcurrentMapCacheManager: JDK의 ConcurrentHashMap을 캐시 저장소로 사용하는 캐시 매니저이다. 별도의 설정을 하지 않으면 기본으로 ConcurrentHashMap을 통해 캐싱된다. 하지만 캐시 관리에서 필요한 다양한 기능들(ex, TTL, TTI)이 부족하여, 실제 서비스에서 사용하기에 기능이 빈약하다.
- SimpleCacheManager: 기본적으로 제공하는 캐시가 없고 사용할 캐시를 직접 등록하여 사용하기 위한 캐시 매니저이다.
- EhCacheCacheManager: 자바에서 유명한 캐시 프레임워크 중 하나인 EhCache를 지원하는 캐시 매니저이다.
- CaffeineCacaheManager: 자바8로 Guava 캐시를 재작성한 Caffeine 캐시를 사용하는 캐시 매니저이다.
- CompositeCacaheManager: 한 개 이상의 캐시 매니저를 사용하도록 지원하는 혼합 캐시 매니저이다.
- JCacheCacheManager: JSR-107 기반의 캐시를 사용하는 캐시 매니저이다.
보편적으로 사용되는 것은 ConcurrentHashMap, EhCache, Caffeine이고 ConcurrentHashMap는 TTL등 세부 기능이 존재하지 않으므로, EhCache와 Caffeine 중 고려했다. 이 두 가지에 대해 더 자세히 알아보자.
EhCache
- 가장 널리 사용되는 자바 기반 캐시이다.
- LRU, LFU, FIFO 제거 알고리즘을 제공한다.
- 3개의 스토리지에 저장 가능
- 힙 메모리(인메모리): JVM의 힙 메모리에 저장한다.
- OffHeap: 메모리 힙 외부에 추가적인 유형의 메모리 저장소를 사용할 수 있다. 자바 GC가 적용되지 않는 저장소이다.
- 디스크: File 캐시와 유사하게 데이터가 디스크에 저장된다.
Caffeine
- Guava Cache와 ConcurrentHashMap을 개선한 ConcurrentLinkedHashMap을 바탕으로 구현된 캐시이다.
- 최적의 적중률을 제공하는 Window TinyLfu 제거 정책을 사용한다. 이를 통해 높은 처리 속도를 보여준다.
Caffeine의 Window TinyLfu
EhCache의 제거 방식은 LRU, LFU, FIFO 3가지 알고리즘을 사용한다. 반면 Caffeine 캐시는 거의 최적의 적중률을 제공하는 Window TinyLFU 방식을 사용한다.
Caffeine 캐시의 Window TinyLFU 방식은 높은 히트율을 유지하면서도 메모리 사용량을 최적화한다. 이는 LFU 기반의 알고리즘이지만 LRU의 장점도 결합한 형태이다.
- Window는 단기 캐시 영역으로, 최근에 사용된 데이터가 자주 사용될 가능성이 높다는 가정에 기반하여 LRU 방식으로 관리된다. 즉, 새로 추가되는 캐시 데이터는 먼저 Window 영역에 저장된다.
- Window 영역이 가득 차면, LRU 알고리즘에 따라 가장 오래된 항목이 제거되고, 이 제거된 항목에 대해 TinyLFU가 빈도를 계산하여, 자주 사용되었다고 판단되면 Main 영역의 Probation Cache으로 옮겨진다. 이곳에서는 LFU 방식으로 관리된다.
- Probation Cache에서 더 자주 사용된 데이터는 Protected Cache 영역에 들어가고 이 곳도 LFU 방식으로 관리된다.
Reference