1차 캐시는 EntityManager 수준에서 관리되며, 동일한 생명주기를 가집니다. 즉, 일반적으로 스레드간 공유가 되지 않으며, 하나의 트랜잭션 내부에서 사용됩니다. 또한, JPA 를 사용한다면 기본적으로 사용되는 캐시입니다.
하지만 2차 캐시는 EntityManagerFactory 수준에서 관리되며, 동일한 생명주기를 가집니다. 즉, 여러 스레드(EntityManager)에서 공유될 수 있으며, 애플리케이션 전역적으로 사용됩니다. 또한, 따로 설정을 해주지 않는다면 적용이 되지 않는 캐시입니다.
2차 캐시는 캐시 저장소로 여러 구현체(Ehcache, HazelCast 등)를 사용할 수 있으며, 추상화를 통해 간편하게 사용할 수 있도록 지원합니다.
주요 특징 - hibernate 기준
글로벌 범위
2차 캐시는 EntityManagerFactory 수준에서 관리되며, 동일한 생명주기를 가집니다. 즉, 여러 스레드(EntityManager)에서 공유될 수 있으며, 애플리케이션 전역적으로 사용됩니다.
캐시 데이터
2차 캐시는 기본적으로 Entity를 캐시하며, hibernate 의 경우 Collection, Query Result 등의 데이터를 캐싱할 수 있습니다.
Entity - Entity 단위로 캐시하며, 식별자 값으로 엔티티를 조회하거나 컬렉션이 아닌 연관된 엔티티를 로딩할 때 사용합니다. Collection - Entity 와 연관된 Collection(Entity 들) 을 캐시하며, 컬렉션이 엔티티를 담고 있으면 식별자 값만 캐싱합니다. Query - 쿼리와 파라미터 정보를 Key 로 사용하여 캐시하며, 결과가 엔티티면 식별자 값만 캐싱합니다.
캐시 동시성 전략
Cache 를 사용할 때, 여러 트랜잭션이나 스레드에서 동시에 캐시에 read & write 연산을 할 경우, 동시성 문제가 발생할 수 있습니다. @Cache 어노테이션의 usage 속성을 통해 캐시 동시성 전략을 제어할 수 있습니다.
NONSTRICT_READ_WRITE - 원본 데이터가 변경될 가능성이 있지만, 적은 빈도로 발생하거나 일관성이 유지되지 않아도 괜찮을 때 사용합니다. - 데이터 변경시(트랜잭션 커밋시) 캐시된 데이터가 무효화되지만, 즉각적인 반영이 보장되지는 않습니다. - 즉, 데이터가 변경되었더라도 캐시에 이전 데이터가 남아있어 다른 트랜잭션에서 이전 데이터를 읽을 수 있습니다. - 데이터 일관성보다는 성능을 우선시 할 때 사용됩니다.
READ_WRITE - 원본 데이터가 변경될 가능성이 있고, 캐시된 데이터의 일관성이 보장되어야 할 때 사용합니다. - 단, optimistic lock 혹은 pessimistic lock / distribution lock 을 함께 설정해야 데이터의 일관성이 보장됩니다. - 데이터 변경시 캐시된 데이터가 즉시 무효화되며, READ_COMMITED 정도의 격리 수준을 보장합니다. - 즉, 데이터가 변경되는 시점에 캐시도 변경되어 다른 트랜잭션에서 최신 데이터를 읽을 수 있습니다. - 성능보다는 데이터 일관성을 우선시 할 때 사용됩니다.
TRANSACTIONAL - 주로 분산 환경에서 사용되며, 트랜잭션 범위 내에서 데이터 일관성이 보장되어야 할 때 사용합니다. - 분산 트랜잭션에 대한 처리를 Transaction Manager 와 통합하여 지원함으로서, 분산 환경에서의 transaction 처리를 쉽게 해줄 수 있도록 지원합니다.
기본적인 동작 방식
출처 - 자바 ORM 표준 JPA 프로그래밍(김영한 저)
1차 캐시 -> 2차 캐시 -> DB 순으로 조회
이 때, 2차 캐시에 저장되는 값들은 해당 엔티티의 속성값들이며, 1차 캐시에는 해당 속성값을 바탕으로 매번 다른 인스턴스가 생성됩니다.
JPA 의 2차 캐시와 Spring Cache 는 애플리케이션 전반에서 Cache 를 사용할 수 있도록 해주며, 추상화와 어노테이션 기반의 설정으로 쉽게 Cache 를 적용할 수 있습니다.
하지만, JPA 의 2차 캐시의 경우는 엔티티를 주로 캐싱하고, Spring Cache 는 AOP 기반으로 메서드의 다양한 결과값을 캐싱할 수 있기 때문에, 좀 더 유연하게 사용할 수 있습니다. 또한, JPA 의 2차 캐시는 당연하게도 JPA 를 사용해야 한다는 제약이 있지만, Spring Cache 는 Spring 기반의 애플리케이션이라면 언제든 사용할 수 있고, 지원하는 Cache 구현체도 많습니다.
이런 이유로, JPA 의 2차 캐시보다는 Spring Cache 를 좀 더 많이 사용하는 것 같습니다.