✔ 깃허브 소스코드
✔ Udemy 강의영상
영속성 컨텍스트(Persistence Context)의 내부에는 엔티티를 보관하는 저장소가 있는데 이것을 1차 캐시(First Level Cache)
라고 부릅니다. 1차 캐시는 트랜잭션이 시작하고 종료할 때까지만 유효합니다. 즉, 트랜잭션 단위의 캐시입니다. 따라서 애플리케이션 전체로 보면 데이터베이스 접근 횟수를 획기적으로 줄이지는 못합니다.
JPA 구현체들은 애플리케이션 단위의 캐시를 지원하는데 이것을 공유 캐시
또는 2차 캐시(Second Level Cache)
라고 부릅니다. 2차 캐시를 적용하면 애플리케이션 조회 성능을 향상할 수 있습니다.
1차 캐시는 영속성 컨텍스트 내부에 있습니다. 엔티티 매니저로 조회하거나 변경하는 모든 엔티티는 1차 캐시에 저장됩니다. 트랜잭션을 Commit 하거나 Flush 를 하게 되면 1차 캐시에 있는 엔티티의 변경 사항들을 데이터베이스에 반영합니다.
JPA를 스프링 프레임워크 같은 컨테이너 위에서 실행하면 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션을 종료할 때 영속성 컨텍스트도 종료합니다.
1차 캐시는 활성화하거나 비활성화할 수 있는 옵션이 아니고 영속성 컨텍스트 자체가 사실상 1차 캐시입니다.
2차 캐시는 애플리케이션 단위의 캐시입니다. 따라서 애플리케이션을 종료할 때까지 캐시가 유지됩니다. 2차 캐시를 사용하면 엔티티 매니저를 통해 데이터를 조회할 때 우선 2차 캐시에서 찾고 없으면 데이터베이스에서 찾게 됩니다. 그래서 2차 캐시를 적절히 활용하면 데이터베이스 조회 횟수를 획기적으로 줄일 수 있습니다.
2차 캐시는 동시성을 극대화하려고 캐시한 객체를 직접 반환하지 않고 복사본을 만들어서 반환합니다. 만약 캐시한 객체를 그대로 반환하면 여러 곳에서 같은 객체를 동시에 수정하는 문제가 발생할 수 있는데 이 문제를 해결하려면 객체에 락을 걸어야 하는데 이렇게 하면 동시성이 떨어질 수 있습니다. 락에 비하면 객체를 복사하는 비용은 아주 저렴하므로 2차 캐시는 복사본을 반환하게 됩니다.
Hibernate 가 지원하는 캐시는 크게 3가지가 있습니다.
1. 엔티티 캐시
2. 컬렉션 캐시
3. 쿼리 캐시
의존성 설정
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
</dependency>
프로퍼티 설정
spring.jpa.properties.hibernate.cache.use_second_level_cache = true
💨 2차 캐시 활성화합니다.
spring.jpa.properties.hibernate.cache.region.factory_class
💨 2차 캐시를 처리할 클래스를 지정합니다.
spring.jpa.properties.hibernate.generate_statistics = true
💨 하이버네이트가 여러 통계정보를 출력하게 해주는데 캐시 적용 여부를 확인할 수 있습니다.
- L2C puts: 2차 캐시에 데이터 추가
- L2C hits: 2차 캐시의 데이터 사용
- L2C miss: 2차 캐시에 해당 데이터 조회 실패 -> 데이터베이스 접근
spring:
jpa:
properties:
hibernate:
generate_statistics: true
format_sql: true
cache:
use_second_level_cache: true
region:
factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
javax:
persistence:
sharedCache:
mode: ENABLE_SELECTIVE
logging:
level:
net:
sf:
ehcache: debug
엔티티 캐시와 컬렉션 캐시
@Cacheable
💨 엔티티 캐시 적용시 사용하는 어노테이션
@Cache
💨 하이버네이트 전용입니다. 캐시와 관련된 더 세밀한 설정을 할 때 사용합니다. 또한 컬렉션 캐시를 적용할 때에도 사용합니다.
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Entity
public class Course {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@OneToMany(mappedBy = "course")
private List<Review> reviews = new ArrayList<>();
...
}
CacheConcurrencyStrategy 속성
READ_ONLY
💨 자주 조회하고 수정 작업을 하지 않는 데이터에 적합합니다.
READ_WRITE
💨 조회 및 수정 작업을 하는 데이터에 적합합니다. Phantom Read 가 발생할 수 있으므로 SERIALIZABLE 격리 수준에서는 사용할 수 없습니다.
NONSTRICT_READ_WRITE
💨 거의 수정 작업을 하지 않는 데이터에 적합합니다.