Skip to content
KwonNam Son edited this page Jun 29, 2014 · 23 revisions

hibetnate4-memcached 시작하기

여기서는 spymemcached-adapter를 기준으로 가장 빠르게 설정하는 방법을 공유한다. JPA를 중심으로 설명한다.

  • hibernate4-memcached는 Cache Region(Namespace) 개념을 지원하기 때문에 Region eviction이 올바르게 작동하며(다른 대분분의 memcached 구현체는 Region Eviction이 안 됨),
  • 분산 캐시 사용시 Entity Class에 구조 변경이 있으면 serialVersionUID 값을 비교하여 버전 변경시 캐시가 없은 것으로 간주하고 캐시를 재생성하게 처리되어 있다(이 처리가 없으면 Entity Class 버전 변경시 오류 발생). 따라서 클래스의 필드 구조가 변경되면 꼭 serialVersionUID를 변경해야 하며, 가능하면 JVM이 스스로 serialVersionUID 값을 생성하게 놔두는게 좋다.

0.4.RELEASE 버전 현재 동시성 전략은 READ_ONLYNONSTRICT_READ_WRITE만 지원하며 읽기 전용 엔티티가 아니라면 NONSTRICT_READ_WRITE를 기본 전략으로 사용하면 된다.

Maven/Gradle 의존성 추가

Home 위키 페이지의 Dependency를 기준으로 Maven/Gradle 등에 의존성을 추가한다.

기본 모듈

Hibernate4Memcache는 총 세가지 단계의 모듈로 구성된다.

  • Hibernate Second Level(L2) Cache 구현체 : Hibernate4MemcachedRegionFactory 클래스로 전체의 뼈대이다.
  • MemcachedAdapter : Java용 Memcached(혹은 유사 캐시)에 접속하고 데이터를 주고받는 구현체이다. 현재는 spymemcached에 대한 구현체인 SpyMemcachedAdapter만 존재한다.
  • Transcoder : MemcachedAdapter들은 Java 객체를 Memcached에 저장하고 읽어올 때(직렬화/역직렬화) 포맷을 지정해줘야 한다. 즉, 자바 객체를 byte[]로 만들고 byte[]를 Java 객체로 변환하는 변환기가 필요하다. 현재 SpyMemcachedAdapter에 대해 KryoTranscoder를 제공하고 있다.

JPA/Hibernate 설정

JPA EntityManagerFactory(코드로 작성할 수도 있고 persistence.xml에 직접 기술할 수도 있음) 혹은 Hibernate SessionFactory에 프라퍼티를 다음과 같이 설정한다.

Hibernate Properties

모든 Hibernate 프라퍼티의 키는 org.hibernate.cfg.AvailableSettings에서 상수로 존재한다.

hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=true
hibernate.cache.default_cache_concurrency_strategy=NONSTRICT_READ_WRITE
hibernate.cache.region.factory_class=kr.pe.kwonnam.hibernate4memcached.Hibernate4MemcachedRegionFactory
hibernate.cache.region_prefix=[원하는 Cache Region Prefix]
hibernate.cache.provider_configuration_file_resource_path=META-INF/h4m-properties.xml
hibernate.cache.use_structured_entries=false
  • hiberante.cache.region_prefix : 만약 하나의 Memcached 서버군에서 여러 종류의 JPA PersistenceUnit(혹은 여러 종류의 Hibernate Session Factory)가 존재할 때 서로 다른 region_prefix를 지정해주어 키가 겹치지 않도록 한다.
  • hibernate.cache.provider_configuration_file_resource_path는 각 캐시 Region별 만료(expiry) 시간 정보를 넣는 Hibernate4Memcached용 설정파일을 지정한다. 이 파일은 Class Path에서 읽어오며, 맨 처음 시작을 /로 하면 안된다. 일반 properties 파일과 XML properties 파일 둘 다 지원한다. 생략하고 EntityManagerFectory나 SessionFactoryManager 프라퍼티 설정에 모든 값을 넣어도 상관 없다.

Hibernate4MemcachedRegionFactory Properties

프라퍼티 키는 kr.pe.kwonnam.hibernate4memcached.Hibernate4MemcachedRegionFactory에 상수로 존재한다.

h4m.adapter.class=kr.pe.kwonnam.hibernate4memcached.spymemcached.SpyMemcachedAdapter

SpyMemcachedAdapter Properties

프라퍼티 키는 kr.pe.kwonnam.hibernate4memcached.spymemcached.SpyMemcachedAdapter에 상수로 존재한다.

h4m.adapter.spymemcached.hosts=localhost:11211,yourhost:11211
h4m.adapter.spymemcached.hashalgorithm=KETAMA_HASH
h4m.adapter.spymemcached.operation.timeout.millis=5000
h4m.adapter.spymemcached.transcoder=kr.pe.kwonnam.hibernate4memcached.spymemcached.KryoTranscoder
h4m.adapter.spymemcached.cachekey.prefix=h4m
h4m.adapter.spymemcached.kryotranscoder.compression.threashold.bytes=20000
  • h4m.adapter.spymemcached.hosts : memcached 서버접속정보를 쉼표로 나열
  • h4m.adapter.spymemcached.hashalgorithm : net.spy.memcached.DefaultHashAlgorithm enum 값
  • h4m.adapter.spymemcached.operation.timeout.millis : Memcached Operation timeout in milliseconds
  • h4m.adapter.spymemcached.transcoder : spymemcached 객체 직렬화/역직렬화 transcoder
  • h4m.adapter.spymemcached.cachekey.prefix : 캐시 키 생성시 맨 앞에 붙일 prefix. 생략 가능.
  • h4m.adapter.spymemcached.kryotranscoder.compression.threashold.bytes : KryoTranscoderLz4로 압축을 하는데, 여기서 지정한 bytes 이상이어야 압축을 수행한다. 그렇지 않으면 압축하지 않은 상태로 데이터 저장한다. 이 값을 너무 작게 설정하면 압축률은 낮으면서 CPU만 점유한다. 경험상 20~30kb 정도는 돼야 했다.

Cache Region 만료(expiry) 시간 설정

Hibernate4Memcached 프라퍼티 설정과 SpyMemcached 프라퍼티 설정 그리고 Cache Region 만료 시간 설정은 hibernate.cache.provider_configuration_file_resource_path 프라퍼티로 지정한 파일에서 하거나 해당 설정 파일을 생략하고 모든 설정을 EntityManagerFactory 혹은 SessionFactory 프라퍼티 설정으로 해도 상관없다.

단, 설정 파일을 지정했고 Factory의 프라퍼티 설정과 설정 파일 설정이 동시에 존재할 경우 설정 파일 설정이 우선한다.

개인적으로는 JPA 사용시 따로 설정파일을 만드는 것 보다는 persistence.xml에 모든 설정을 넣는 것을 더 선호한다.

h4m.expiry.seconds=600 # 10분
h4m.expiry.seconds.[CacheRegionPrefix].org.hibernate.cache.spi.UpdateTimestampsCache=86400 # 하루
h4m.expiry.seconds.[CacheRegionPrefix].org.hibernate.cache.internal.StandardQueryCache=3600 # 1 시간
h4m.expiry.seconds.[CacheRegionPrefix].[region]=1800 # 30분
  • h4m.expiry.seconds : Region에 대한 만료 시간을 지정 안했을 경우 기본값으로 사용되는 만료시간.
  • [CacheRegionPrefix] : 이는 위에서 hiberante.cache.region_prefix로 설정한 값으로 대체 할 것.
  • [region] : 각 캐시의 region으로 지정한다. 각 Entity Region은 보통 해당 Entity 클래스의 Fully Qualified Class Name이며, 이 FQCN은 지나치게 기므로 가능하면 명확하게 Region 이름을 정해주는 것이 좋다.
  • h4m.expiry.seconds.[CacheRegionPrefix].org.hibernate.cache.spi.UpdateTimestampsCache : 캐시의 데이터 정합성을 맞추는데 사용하는 Hibernate의 기본 UpdateTimestampsCache 캐시
  • h4m.expiry.seconds.[CacheRegionPrefix].org.hibernate.cache.internal.StandardQueryCache : 사용자 정의 쿼리의 결과를 저장하는 기콘 쿼리 캐시 Region. 쿼리에 대해서도 캐시 Region을 명시할 수도 있다.

Entity에 캐시 적용

실제 Cache 적용시 JPA 애노테이션 보다는 Hibernate의 @org.hibernate.annotations.Cache 애노테이션 사용을 더 권한다.

@org.hibernate.annotations.Cache(region = "region지정", usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public Class YourEntity implements Serializable {
    // serialVersionUID는 지정하지 않고 JVM이 스스로 결정하게 두는 것을 권한다.

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentId")
    @org.hibernate.annotations.Cache(region = "관계용 region지정" usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    private List<Child> children;
}
  • Entity 자체에 지정하고 엔티티와 연관을 맞는 데이터 목록도 캐시하고자 한다면 연관 부분에도 @Cache를 명시해야 한다.
  • 모든 Entity는 Serializable을 구현해야한다.
  • serialVersionUID 명시는 권하지 않는다. JVM이 자동 생성해주는 것이 더 편리하고 안전하다. Eclipse에서 경고가 발생하면 클래스에 @SuppressWarnings("serial") 지정한다. 만약 이 값을 명시했다면 클래스가 변경 될 때마다 개발자가 철저하게 값을 변경해주어야 에러 없이 캐시가 가능해진다.

Query에 캐시 적용

일반적인 쿼리

  • Criteria나 JPQL, HQL 등으로 쿼리를 생성할 때 Hint로 org.hibernate.cacheable 값을 true로 지정하면, 기본 쿼리 캐시 Region([CacheRegionPrefix].org.hibernate.cache.internal.StandardQueryCache)에 캐시된다.
  • org.hibernate.cacheRegion Hint에 캐시 region 이름을 지정하면 원하는 Region에 캐시를 저장할 수 있다. 굳이 쿼리에 대해 region을 지정할 필요는 없어 보인다.
  • Hibernate는 Query Cache Key를 매우 길게(쿼리 문자열 전체와 부가 정보를 Key로 지정함) 생성해서 memcached의 key 길이 제한(250 bytes)를 넘기는 경우가 생긴다. hibernate4-memcached는 Query Cache Key를 MD5와 해당 키 문자열의 hashCode() 값을 조합하여 새로운 키를 생성한다.

Named Query

Query Hint 종류는 같다. 다음과 같은 방식으로 힌트를 지정한다.

@Entity
@Table(name = "books")
@NamedQuery(name = "Book.byEdition",
        query = "from Book where edition=:edition",
        hints = {
                @QueryHint(name = "org.hibernate.cacheable", value = "true"),
                @QueryHint(name = "org.hibernate.cacheRegion", value = "book-by-named-edition")
        }
)
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "books")
public class Book implements Serializable {

Spring Data JPA

Query Hint 종류는 같다. 다음과 같은 방식으로 Repository Interface에 쿼리 힌트를 주면 된다.

@QueryHints({@QueryHint(name = "org.hibernate.cacheable", value = "true") })
List<YourEntity> findBySomething(Param yourparam);

JPA Query DSL

Query Hint 종류는 같다. 다음과 같이 Hint를 지정한다.

final JPAQuery yourEntityQuery = (JPAQuery)from(qYourEntity);
yourEntityQuery.setHint("org.hibernate.cacheable", "true");

주의할 점

  • Region 이름은 Memcached의 Key로 사용되므로 memcached key로 사용할 수 있는 문자열로 지정해야한다.
  • serialVersionUID는 아예 지정을 하지 않고 JVM이 스스로 판단하게 하거나 만일 직접 지정했다면 항상 클래스 변경시마다 값을 철저히 바꿔줘야한다.
  • Memcached 자체 제약 사항들을 주의해야한다.
  • Memcached key byte max size : 250 bytes
  • Memcached max expiry seconds : 30일(2592000 초)
  • Memcache Key로 지정할 수 없는 문자가 존재할 수 있음.

참조