엔티티 구성요소 - 2 BytecodeEnhancement - Dirty Tracking 분석

엔티티 구성요소 - 2 BytecodeEnhancement - Dirty Tracking 분석

JPA의 기본 사항을 정리한다.

Hibernate가 Entity를 추적하는 방법

Hibernate가 엔티티에서 변경된 사항들을 어떻게 추적할까?
그리고 플러시 시점에 무슨 일이 일어나고 성능저하 포인트가 발생할까?

Entity는 DB에서 로드된 시점에 트랜잭션의 영속성 컨텍스트에 스냅샷이 만들어진다.
런타임시에 Entity의 상태는 얼마든지 변경될 수 있다.
그리고 DB에 저장될 때 (flush) 관리되고 있는 모든 속성은 로드 시점의 스냅샷과 비교된다.
이 시점에서 Hibernate가 Entity 전체를 대조해서 변경점을 수색하는 과정이 Dirty-Checking 메커니즘이다.
이 메커니즘은 Java의 Reflection을 사용하여 이루어진다.

영속성 컨텍스트에 스냅샷이 찍힌 관리 엔티티가 적은 경우, 이 과정은 눈에 띄지 않는다.
그러나 관리하는 Entity가 많으면 당연하게도 대조에 더 많은 CPU와 메모리를 사용한다.
스냅샷때문에 영속성 컨텍스트가 관리하는 Entity는 일반적으로 두 배의 메모리가 필요하기 때문이다.

이런 Dirty-Checking 메커니즘의 비효율성을 보완하기 위해 BytecodeEnhancement - DirtyTracking 이 등장했다.

BytecodeEnhancement는 특정 목적을 위해 Java Class의 바이트코드를 조작하는 기술이다.
이는 컴파일, 런타임 모두 수행될 수 있다.
컴파일 시점에 수행되면, 컴파일 이후에 자동 더티체킹을 구현하는 바이트코드 수준의 메서드를 각 Entity 클래스에 심어둔다.
빌드 시점에 수행되기 때문에 런타임 시점에 성능 저하는 없다.


Dirty Tracking 이후 변화된 Entity 추적 변화

Hibernate의 Dirty-Tracking을 활성화 시키면, 직접 대조해서 변경점을 찾는 대신
심어둔 바이트코드 메서드로 Entity 자체에게 변경된 점을 물어봐 처리한다.

성능 개선점

  1. 1000개의 Entity를 관리하고, 500개 Entity의 String 필드 수정(+13.5%) 1000개 1000개 Entity의 String 필드 수정

  2. 5000개 Entity를 관리하고, 1000개 Entity의 String 필드 수정(+10.25%) 5000개 5000개 Entity의 String 필드 수정

빌드 시간 차이

  1. Entity가 1000개인 경우

약 6초정도 컴파일 타임이 추가되어 오버헤드 21% 증가했다.
더티 트래킹 활성화로 얻는 이점을 보면 빌드시간 6초 증가의 페널티를 감수하고 활성화할 가치는 충분하다고 본다.


@DynamicUpdate와 상관관계가 있을까?

보통 Entity가 Update되면 모든 필드가 업데이트된다.
@DynamicUpdate는 변경된 필드만 업데이트하는 기능이다.
엔티티의 컬럼이 많을 때, DB가 컬럼 단위의 locking을 지원할 때 사용하면 리소스 절약과 lock 문제 발생 확률이 줄어드는 이점이 있다.

@DynamicUpdate의 비용

@DynamicUpdate를 엔티티에 선언해주면 Hibernate는 캐싱되어있는 SQL 문장을 뽑아 쓰지 않고 손수 만든다.
즉, 변경 시점에 엔티티 수색을 해서 차이점을 추적해야 한다는 것인데 여기서 오버헤드가 발생한다.

그렇다면 과연 추적과 수색에서 발생하는 오버헤드를 줄이는 DirtyTracking을 활성화시키면 @DynamicUpdate의 비용을 최적화할 수 있지 않을까?

답변없는DynamicUpdate와DirtyTracking관계 메아리도 울리지 않는 공허한 DynamicUpdate와 DirtyTracking 질문


나중에 연구해봐야겠다.


실습

하이버네이트 6.0.0 기준으로 조금 변경된 설정 방법이다.
문구가 좀 더 간결해지고 직관적으로 수정된 것 같다.

plugins {
    id "org.hibernate.orm" version '6.4.4.Final'
}

hibernate {
   enhancement {
      // for illustration, enable them all
      lazyInitialization true
      dirtyTracking true
      associationManagement true
   }
}

설정 확인

실제 디버깅을 해보거나 인텔리제이 기준으로 build파일에서 클래스를 뒤져보면, 비영속 상태로 여러 메서드가 삽입된 것을 확인할 수 있다.

디버깅모습 디버깅시 발견할 수 있는 삽입된 메서드

빌드된객체모습 빌드된 객체 모습


참고자료