Server/Spring JPA

JPA - 영속성(persistence란) [+1차 캐시]

JaeHoney 2022. 3. 15. 23:01

영속성

Spring JPA 로고를 보면 영속성(persistence)라는 단어가 크게 그려져 있습니다. 그만큼, 영속성 없이 Spring JPA를 얘기하기 힘듭니다. 영속성은 쉽게 말하면 지속을 의미합니다. 더 자세히 풀자면, 엔터티를 사용하고 바로 폐기하는 것이 아니라, 저장을 해서 지속적으로 사용하며 얻는 이점을 얻는 것을 말합니다. 

 

예를 들어, JPA에서 repository.findOne을 동일한 데이터에 대해 조회를 5번 요청합니다. 하지만, DB의 쿼리 로그나 JPA가 기록한 로그를 보면, SELECT문이 1번만 실행됩니다.

 

그 이유가 영속성입니다. 첫번째 SELECT한 결과를 JPA의 1차 캐시에 보관하고 계속 재사용하기 때문입니다.

 

1차 캐시

JPA에서 1차 캐시는 EntityManager가 관리하는 영속성 컨텍스트 내부에 있는 첫 번째 캐시입니다. 

EntityManager는 Thread-safety하지 않으므로, per thread로 바인딩됩니다. 즉, 각 쓰레드의 1차 캐시는 별도로 동작하는 것을 전제로 합니다.

 

1차 캐시의 조회 동작은 아래와 같습니다.

  1. 조회 시 1차 캐시에 데이터가 이미 있는지 확인하고, 데이터가 있으면 가져온다. (비교는 PK로 한다.)
  2. 1차 캐시에 데이터가 없다면, 데이터베이스에 데이터를 요청한다.
  3. 데이터베이스에서 받은 데이터를 다음에 재사용할 수 있도록 1차 캐시에 저장한다.

 

Q] Update가 발생하면 데이터가 갱신될텐데, 캐시에 있는 것을 조회하면 과거의 데이터만 받는거 아닌가요?

A] 1차 캐시는 영속성 컨텍스트 내부에 있습니다. 영속성 컨텍스트 내부에 있는 엔터티의 변화가 즉시 1차 캐시에 저장되기 때문에, 캐시에서 갱신된 데이터를 받게 됩니다.

 

변경 감지

영속성 컨텍스트가 관리하는 엔터티가 수정되면 지연 SQL 저장소쿼리문이 저장됩니다. 

 

플러시

Flush가 발생하면 쌓인 지연 SQL 저장소에 있는 쿼리들을 즉시 DB에 날립니다. 즉, 그동안의 변경사항을 Flush가 일어날 때 한번에 기록합니다. 따라서, DB 접근 빈도를 최소화할 수 있습니다. 이를 쓰기 지연이라고 합니다.

 

아래의 3가지 경우 중 1가지라도 발생되면 Flush가 발생합니다.

  • Transaction.commit()
  • entityManager.flush()
  • JPQL 쿼리 실행

쿼리문을 모아서 보낼 단위는 hibernate.jdb.batch_size속성의 수치를 변경해서 설정할 수 있습니다.

 

즉, 쓰기 동작은 아래와 같이 동작합니다.

  1. 데이터가 변경되면 즉시 1차캐시에 반영한다.
  2. 변경 사항이 지연 SQL 저장소에 저장된다.
  3. Transaction이 commit되면 Flush가 발생한다.
  4. 지연 SQL 저장소에 있는 SQL문을 DB에 요청한다.

 

이런 영속성은 장점이기도 하지만, JPA를 조심해서 사용해야 하는 이유입니다.

 

예를 들어, JPA는 save()문이나 update()문을 사용하지 않아도, 트랜잭션이 커밋되면 엔터티의 변경을 감지해서 DB를 갱신합니다. 변경이 반영되지 않겠다고 생각해서, 엔터티의 필드를 변경하다간 큰일날 수 있습니다.

 

감사합니다.