나누고 싶은 개발 이야기

Data Engineer로서 기록하고 공유하고 싶은 기술들. 책과 함께 이야기합니다.

Framework/spring

[spring] cache

devidea 2017. 8. 29. 14:44
캐시는 미래의 데이터 요청에 빠르게 응답하기 위한 데이터 저장의 한 방법이다. (wikipedia)
캐시의 활용은 다양한데, 동일한 데이터를 계속해서 활용할 경우 계산 작업을 중복해서 하지 않도록 하거나 계산 시간이 오래 걸리는 작업을 미리 처리하고 추후 요청이 왔을 때 응답하도록 할 수 있다.

spring에서는 cache 서비스를 추상화하며 여러 데이터 저장소를 활용할 수 있도록 구성하였다.
org.springframework.cache.Cache 와 org.springframework.cache.CacheManager 인터페이스로 구체화된다.

해당 추상화에 대한 여러가지 구현들이 있는데 우리는 가장 기본이 되는 JDK의 java.util.concurrent.ConcurrentMap를 활용한 구현법을 살펴보자. 그 외에도 Redis, Couchbase, JCache, EhCache 2.x 등 여러 종류가 있으니 자세한 사항은 spring 문서를 찾아보도록 하자.

지금부터는 annotation을 사용해 선언적 방법으로 cache 설정을 해보도록 하자.
  • @Cacheable : 캐시 설정
  • @CacheEvict : 캐시 삭제
  • @CachePut : 캐시 업데이트
  • @Caching : 여러개의 cache 동작을 적용
  • @CacheConfig : 클래스 레벨에서 캐시 설정의 공유 

@Cacheable
캐시 하고자 하는 메서드에 @Cacheable를 선언하면 해당 메서드가 캐싱이 된다. 메서드가 수행되었는지 판단하고 수행되었다면 저장한 결과를 리턴하도록 한다. 선언을 할 때 연관된 이름이 필요하다. 이름은 cacheNames로 정의한다.
@Cacheable("books")
public Book findBook(ISBN isbn) {…}

cache는 기본적으로 key-value 구조로 되어 있다. 저장된 캐시에 접근하기 위해서는 적절한 key값이 필요하다. key값을 지정하지 않을 경우 아래와 같은 알고리즘으로 구현된 KeyGenerator에 의해서 key값을 만들어준다.
  • 파라미터가 없을 경우, SimpleKey.EMPTY
  • 파라미터가 하나일 경우, 인스턴스를 리턴한다.
  • 파라미터가 하나 이상일 경우, 모든 파라미터를 포함한 SimpleKey를 리턴한다.
아래 코드는 key 값을 지정해 준 것이다.
키를 설정한 방법은 Spring Expression Language (SpEL)를 따른다. 
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

만약 동일한 파라미터를 가진 2개의 함수에 key값을 지정하려고 하면 어떻게 할까?

다음 stackoverflow에서는 2가지의 해결법을 제안하고 있다.

[방법 1]. KeyGenerator 구현을 통해 key 구분
public class CustomKeyGenerator implements KeyGenerator{
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder key = new StringBuilder();
        //include method name in key
        key.append(method.getName());
        if (params.length > 0) {
            key.append(';');
            for (Object argument : params) {
                key.append(argument);
                key.append(';');
            }
        }
        return key.toString();
    }
}

앞서 기본 KeyGenerator가 있다고 했는데, 함수이름과 파라미터를 활용한 새로운 KeyGenerator를 구현하는 것이다.

새롭게 구현된 CustomKeyGeneratorBean으로 등록하고 @Cacheable 설정시 사용한다. 


[방법 2]. 메서드이름과 파라미터를 활용해 SpEL 설정.

@Component
public class StatService {

   @Cacheable(value="statCalc",key="#root.method.name.concat(':').concat(#param"))
   public int getMeth1(int param) {
      // LOGIC1
   }

   @Cacheable(value="statCalc",key="#root.method.name.concat(':').concat(#param"))
   public int getMeth2(int param) {
      // LOGIC2
   }
}


두번째 방법은 SpEL로 key를 작성시 함수이름과 파라미터를 포함해서 구분된 key값을 만들어 주는 것이다.


@CachePut
캐시의 값을 업데이트 하고자 할 때 사용한다. @CachePut을 선언한 메서드를 항상 실행되고 캐시값을 바꾸도록 되어 있다.
한가지 주의할 점은 @CachePut과 @Cacheable을 동시에 선언하면 안된다. @Cacheable은 캐시를 사용하여 실행을 건너뛸 수 있지만, @CachePut은 실행을 강제한다.


@CacheEvict

@CacheEvict는 캐시를 지우는 역할을 한다. 캐시에서 오래된 데이터 또는 사용하지 않은 데이터를 제거하는데 유용하다. 추가 파라미터로 allEntries는 전체 캐시를 지울지 여부를 선택할 수 있다. 
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)



[참고]


반응형