JPA 1차 캐시와 2차 캐시 소개
네트워크를 통해 데이터베이스에 접근하는 시간 비용은 애플리케이션 서버에서 내부 메모리에 접근하는 시간 비용보다 수만에서 수십만 배 이상 비싸다. 따라서 조회한 데이터를 메모리에 캐시 해서 데이터베이스 접근 횟수를 줄이면 애플리케이션 성능을 획기적으로 개선할 수 있다.
그래서 1차, 2차 캐시가 뭔데?
영속성 컨텍스트 내부에는 엔티티를 보관하는 저장소가 있는데 이것을 1차 캐시라 한다. 이것으로 얻을 수 있는 이점이 많지만 일반적인 웹 애플리케이션 환경은 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다. 따라서 애플리케이션 전체로 보면 데이터베이스 접근 횟수를 획기적으로 줄이지는 못한다.
하이버네이트를 포함한 대부분의 JPA 구현체들은 애플리케이션 범위의 캐시를 지원하는데 이것을 공유 캐시 또는 2차 캐시라 한다.
1차 캐시
1차 캐시는 영속성 컨텍스트 내부에 있다. 엔티티 매니저로 조회하거나 변경하는 모든 엔티티는 1차 캐시에 저장된다.
트랜잭션을 커밋하거나 플러시를 호출하면 1차 캐시에 있는 엔티티의 변경 내역을 데이터베이스에 동기화한다.
일반적으로 JPA는 스프링 프레임워크 같은 컨테이너 위에서 실행하면 트랜잭션을 시작하거나 종료할 때 영속성 컨텍스트도 시작하거나 종료한다.(OSIV 제외)
1차 캐시는 끄고 켤 수 있는 옵션이 아니다. 영속성 컨텍스트 자체가 사실상 1차 캐시다.
위에 이미지처럼 find를 했을 때 해당 엔티티가 1차 캐시에 존재하면 1차 캐시에 저장된 엔티티를 반환하고 존재하지 않으면 DB를 조회한다. 하지만 동일한 트랜잭션 내에서만 1차 캐시가 존재하기 때문에 이미 1차 캐시에 저장되어 있는 경우는 흔치 않다.
2차 캐시
애프리케이션에서 공유하는 캐시를 JPA는 공유 캐시(shared cache)라 하는데 일반적으로 2차 캐시(second level cache, L2 cache)라 부른다.
2차 캐시는 애플리케이션을 종료할 때까지 유지된다. 분산 캐시나 클러스터링 환경의 캐시는 애플리케이션보다 더 오래 유지될 수도 있다.
2차 캐시를 적용하면 엔티티 매니저를 통해 데이터를 조회할 때 우선 2차 캐시에서 찾고 없으면 데이터베이스에서 찾는다. 2차 캐시를 적정히 활용하면 데이터베이스 조회 횟수를 획기적으로 줄일 수 있다.
1차 캐시에서 엔티티를 찾아보고 없으면 2차 캐시에서 엔티티를 찾는다. 찾는 엔티티가 존재하면 2차 캐시에서 반환하고 존재하지 않으면 DB를 조회해서 반환한다. 이때 2차 캐시는 동시성을 극대화하기 위해 캐시 한 객체를 직접 반환하지 않고 복사본을 만들어 반환한다.
만약 캐시한 객체를 그대로 반환하면 여러 곳에서 같은 객체를 동시에 수정하는 문제가 발생할 수 있는데 이 문제를 해결하기 위해 객체에 락을 걸면 동시성이 떨어지는 문제도 있다. 락에 비하면 객체를 복사하는 비용은 아주 저렴하다.
그렇기 때문에 2차 캐시는 객체를 직접 반환하지 않고 복사본을 만들어 반환한다.
1차 캐시와 뭐가 다르냐고 생각할 수 도 있지만 2차 캐시는 애플리케이션 범위의 캐시이므로 데이터베이스 조회가 1차 캐시만 사용할 때 보다 획기적으로 줄어든다.