JPQL 쿼리 혹은 Criteria 쿼리는 AST(Abstract Syntax Tree)으로 파싱된다. 그래야 하이버네이트가 SQL문을 실행할 수 있다.
이와 같은 쿼리 컴파일 시간을 단축시키기 위해 하이버네이트는 Query Plan Cache를 사용한다.
네이티브 쿼리인 경우, 하이버네이트에서 파라미터와 반환 타입에 대한 정보를 추출하여 ParameterMetadata에 저장한다.
모든 실행마다 하이버네이트는 Cache를 확인하여 Query Plan이 있는지 확인한다. 없는 경우에만 Query Plan을 새로 생성한 후, 향후 재사용을 위해 캐시에 값을 저장한다.
Query Plan Cache 설정
Query Plan Cache는 두가지 속성으로 설정할 수 있다.
hibernate.query.plan_cache_max_size
Quern Plan의 최대 개수를 결정한다.
hibernate.query.plan_parameter_metadata_max_size
캐시에 저장할 ParameterMetadata 인스턴스의 수를 관리한다.
각 설정의 기본 값은 2048개이다.
만약 쿼리의 개수가 캐시에서 허용한 수보다 많은 경우, 쿼리를 컴파일하는 데에 시간이 소요되어 쿼리 실행 시간이 늘어날 수 있다.
주의사항
In절 쿼리를 사용하는 경우, Query Plan Cache가 과하게 메모리를 사용하는 문제를 만날 수 있다. 이 때, 서버의 응답이 느려지고, 가비지 컬렉션이 계속 실행되는 현상이 발생하게 된다.
이 때에는 다음과 같은 설정을 추가하면 성능을 개선할 수 있다.
in_clause_parameter_padding=true
이 설정은 여러 In절 쿼리에서 IN절 Query Plan Cache을 재사용할 수 있게 해준다. 매개변수가 달라도, 동일한 In절 쿼리를 사용하게 함으로써 성능을 올린다. (IN 절 쿼리에 대해서 2의 제곱단위로 쿼리를 생성한다.)
해당 설정이 지원하는 기능의 예시는 다음과 같다.
select * from product in (1);
select * from product in (1, 2);
select * from product in (1, 2, 3);
select * from product in (1, 2, 3, 4);
select * from product in (1, 2, 3, 4, 5);
select * from product in (1, 2, 3, 4, 5, 6);
----------------------------------
select * from product in (?);
select * from product in (?, ?);
select * from product in (?, ?, ?);
select * from product in (?, ?, ?, ?);
select * from product in (?, ?, ?, ?, ?);
select * from product in (?, ?, ?, ?, ?, ?);
위와 같이 in 절의 바인딩 파라미터가 동적으로 변경되는 경우, 총 6개의 쿼리 캐시가 메모리에 저장되게 된다. 반면, in_clause_parameter_padding 옵션을 사용하면, 쿼리가 3개로 줄어든다.
select * from product in (1, 1);
select * from product in (1, 2);
select * from product in (1, 2, 3, 3);
select * from product in (1, 2, 3, 4);
select * from product in (1, 2, 3, 4, 5, 5, 5, 5);
select * from product in (1, 2, 3, 4, 5, 6, 6, 6);
---------------------------------
select * from product in (?, ?);
select * from product in (?, ?, ?, ?);
select * from product in (?, ?, ?, ?, ?, ?, ?, ?);
이처럼 in_clause_parameter_padding은 쿼리 플랜을 재사용하여 전체 In절 쿼리 플랜 개수를 줄인다.
이를 통해 쿼리 플랜이 불필요하게 메모리를 모두 점유하는 현상을 없앨 수 있다. 그리고 RDBMS 자체 쿼리 플랜 캐시의 히트율도 높일 수 있다.
참고
- https://www.baeldung.com/hibernate-query-plan-cache
- https://meetup.toast.com/posts/211
- https://kwonnam.pe.kr/wiki/java/hibernate/performance
- https://vladmihalcea.com/improve-statement-caching-efficiency-in-clause-parameter-padding/
'소프트웨어-이야기 > 프로그래밍 언어와 프레임워크' 카테고리의 다른 글
Java14. Record Keyword (0) | 2022.05.14 |
---|---|
Spring과 의존성 주입 (DI) (0) | 2022.04.18 |
Domain services vs Application services (0) | 2021.06.05 |
Hibernate-type-52 - Entity에 Postgresql Jsonb 타입 선언하기 (0) | 2020.11.21 |
[Django] Django에서 MultiProccess 사용 시 주의사항 (0) | 2020.03.08 |