[Kotlin] Redis vs Ehcache 무엇을 쓸까?

노아카프카·2022년 11월 2일
2

Slack 채널에 한 가지 퀴즈가 올라왔다.

Cache 사용이 필요한 상황에서 Redis 대신에 EHcache를 사용하려고 한다.
왜 그럴까? 생각을 이야기 해봅시다. --> Redis 와 EhCache 의 비교

먼저 나는 EHcache에 대해서 들어본 적이 없었다.
그렇기 때문에 먼저 EHcache에 대해서 알아보고, Redis와 EHcache의 일반적인 비교를 해보고, 해당 이슈의 경우엔 어떤 필요 때문에 EHCache를 사용했는지 유추해보자.


1. EHcache 란?

Spring에서 간단하게 사용할 수 있는 Java 기반 오픈소스 캐시 라이브러리
별도의 데몬을 가지지 않고, Spring 내부적으로 동작한다. 이 말은 프로세스 라이프 사이클을 Spring Application과 동일하게 가져간다는 뜻이다. 즉, 스프링이 죽으면 EHcache도 죽는다는 말.

Ehcache는 직렬화된 데이터 객체를 저장하는 메모리 블럭이다. Ehcache는 프로세스 내 캐싱에서 테라바이트 크기의 캐쉬와 함께 프로세스 내/프로세스 외 혼합 구현으로 확장된다.

Ehcache는 아래 세 가지의 공간에 데이터를 저장할 수 있다.
[ Memory / OffHeap / Disk ]

  • Memory Tier : In - Memory
  • OffHeap Tier : Memory Heap 외부에 저장. java GC가 적용 X. 바이트 단위의 매우 큰 캐시를 생성.
  • Disk Tier : File 캐시와 유사. CacheManager를 이용하여 여러 디스크 저장소 경로를 구성 할 수 있다.

Ehcache는 LRU(Least Recent Used), LFU (Least Frequency Used), FIFO(First In First Out) 세가지 제거 알고리즘을 제공한다.

스프링은 캐시 추상화 (Cache Abstraction)을 통해 편리한 캐싱 기능을 지원하고 있다. (cache를 사용할 때, 추상화 인터페이스를 통해서 ehcache, redis 와 같은 다른 캐싱 인프라 간의 전환을 손쉽게 할 수 있다.)

2. 다른 Cache Engine과의 비교

비교해볼만한 Cache Engine에는 redis 와 Memcached 가 있다.

- redis

redis는 별도의 서버를 띄워야 하기에(Global Cache), 네트워크 지연이나 단절 등 네트워크 상에서 생길 수 있는 문제가 발생할 가능성이 생긴다. 또한 redis는 풍부한 자료구조, pub/sub, 싱글 쓰레드를 통한 정합성 유지 등 다양한 기능들을 제공한다.
하지만 단순히 캐싱기능이 필요할 뿐이라면? 오버스펙일 수 있지 않을까?

- memcached

memcached는 같은 로컬환경에서 실행될 수 있지만(Local Cache), Spring Application과는 별도로 구동해야한다. 어차피 라이프 사이클이 분리된다는 말이다.

3. 왜 Ehcache를?

이번 질문이 던져진 이슈에서는 Global Cache / Local Cache를 사용할 때에 어떤 기준으로 선택을 해야하는 지가 중요하다고 생각한다.

다수의 Server(우리 회사의 경우는 k8s의 pods)에서 동시에 해당 데이터에 접근하고, 동시에 데이터 일관성의 유지가 중요하다면 Global Cache를 사용하는 것이 좋아보인다.

반대로, 한 대의 서버에서 데이터에 접근하거나, 여러 대의 서버에서 접근하더라도 데이터 변경의 가능성이 적고, 데이터의 모수가 크지 않다면, Local Cache를 사용하는 것이 (특히 네트워크, 인프라 측면에서)효율적일 것이다.

이번 이슈에서 Cache 선택 기준은 '데이터의 성격' 이었다.
모수가 적고, 가변성이 적은 데이터를 단순 캐싱해야 하는 상황에서 굳이 여러 비용을 감수하면서, Global Cache를 사용할 필요가 없는 것이다.

데이터나 서비스의 성격에 맞춰 합당한 도구를 써야한다. 있으니까 그냥 쓰면 안된다. 상황에 가장 부합하는 도구는 뭐가 좋을지 항상 고민하면서 사용하자.

4. Kotlin Project에 간단하게 적용해보기

여러 자료들을 검색해봤는데 내 능력으로는 코틀린 + 스프링 + Ehcache 자료를 찾기가 쉽지 않았다. 자바로 된 예제를 보고 따라서 작성해보았다.

1) 의존성 추가

//cache
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("org.ehcache:ehcache:3.8.0")
implementation("javax.cache:cache-api:1.1.0")

2) resources/ehcache.xml 파일 생성

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

    <cache alias="simpleCache">
        <key-type>java.lang.String</key-type> 
        <value-type>com.ace.soniceweather.dto.weather.WeatherDTO</value-type>
        <expiry> 
            <ttl unit="minutes">5</ttl> 
        </expiry>
        <resources>
            <heap unit="entries">10</heap>
        </resources>
    </cache>
</config>

3) Application.properties에 ehcache 경로 등록

#Cache
spring.cache.jcache.config=classpath:ehcache.xml

4) Application 클래스에 @EnableCaching 추가

@SpringBootApplication
@EnableCaching
class MyApplication

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}

5) Service 함수에 @Cacheable 추가

  • value -> ehcache.xml 에서 설정한 cache의 alias (String 배열로 입력)
  • key -> cache 값을 구분하는 key. getWeather 함수의 인자인 date로 연결.
@Cacheable(value=["simpleCache"], key = "#date")
fun getWeather(date: String): WeatherDTO {
    println("no cache") //실제로 캐싱을 하는지 확인하기 위한 용도
    return WeatherDTO.of(weatherRepository.findByDate(date))
}

이렇게 코드를 작성하고, application을 실행해보자.
정상적으로 캐싱이 되는 코드는 getWeather 함수를 2번 호출하게 되면,
한 번의 no cache 라는 문자열을 출력하게 된다.

0개의 댓글