문제
개발하다 보면 무리한 join을 회피하기 위해 in 절 쿼리를 사용해야할 경우가 많다.
public interface ExampleRepository extends JpaRepository<Example, Long>{
List<Example> findByIdIn(List<Long> ids);
}
위와 같은 코드를 작성했을 때 다음과 같은 쿼리가 호출될 것이다.
select ... from example where id in (? ,? ,? ...)
그런데 여기서 보면 ids는 고정된 크기가 아니며, 요청된 갯수에 따라서 쿼리문이 길어질 수 있다.
애초에 RDBMS 자체에서 갯수 제한을 두기도 하거니와 너무 많은 요소가 포함되었을 때 DB에 한번의 요청이 너무 많은 작업을 요구하기 때문에 in 절에 너무 많은 갯수를 넣는건 바람직하지 못하다.
그래서 적절한 사이즈로 청킹하여 여러번에 나눠 요청하도록 보통 작업하곤 한다.
만약 ids가 5,789개가 들어왔다고 치고 1,000건씩 청킹하여 쿼리를 요청하도록 코드를 작성했다고 치자.
그러면 다음과 같이 쿼리가 6번 발생할 것이다.
select ... from example where id in (? ,? ,? ...) -- in절 1000개
select ... from example where id in (? ,? ,? ...) -- in절 1000개
select ... from example where id in (? ,? ,? ...) -- in절 1000개
select ... from example where id in (? ,? ,? ...) -- in절 1000개
select ... from example where id in (? ,? ,? ...) -- in절 1000개
select ... from example where id in (? ,? ,? ...) -- in절 789개
하이버네이트에서 DB 접근 및 요청은 결국 내부적으로 jdbc API를 이용한다. 따라서 앞에 5개의 쿼리는 같은 prepareStatement 쿼리를 사용할 수 있어보인다. 매번 새로운 쿼리를 조립하지 않기 위해 하이버네이트에서는 실행되는 쿼리를 따로 메모리에 캐시해둔다.
근데 여기서 문제가 있다. ids가 고정 값이 아니기 때문에 맨 마지막 쿼리에서의 in절 안의 ? 갯수는 매번 바뀐다.
해당 예시의 경우 청크 사이즈가 1,000 이기 때문에 이론상 1,000개의 쿼리 캐시가 메모리에 적재될 수 있다.
해결
하이버네이트의 in_clause_parameter_padding 속성을 설정하여 이 문제를 해결할 수 있다.
설정은 다음과 같이 하면 된다.
application.yml
spring:
jpa:
properties:
hibernate:
query:
in_clause_parameter_padding=true
이 속성을 적용하면 다음과 같이 호출된다.
select ... from example where id in (1001, 1001) -- ids = [1001]
select ... from example where id in (1001, 1002) -- ids = [1001, 1002]
select ... from example where id in (1001, 1002, 1003, 1003) -- ids = [1001, 1002, 1003]
select ... from example where id in (1001, 1002, 1003, 1004, 1005, 1005, 1005, 1005) -- ids = [1001, 1002, 1003, 1004, 1005]
...
prepareStatement 쿼리문 기준으로 in 절의 ? 갯수는 2의 거듭제곱 갯수로만 생성된다.
in 절에 같은 값이 여러 개 들어가도 쿼리의 결과는 똑같다. 이러한 점을 이용하여 한번 만들어 놓은 쿼리를 캐시해두고 같은 쿼리를 사용하게 만들어 캐시 히트율을 올리도록 튜닝하는 옵션이다.
아까 1,000건씩 청킹하는 예시에 대입했을 경우 만들어져서 캐시에 저장되는 쿼리는 최대 10개만 생겨날 것이다. 이러한 방법으로 메모리를 절약할 수 있다.
끝.
'JPA' 카테고리의 다른 글
[JPA] 즉시 로딩과 지연 로딩, 지연 로딩을 써야하는 이유 (0) | 2022.07.26 |
---|