batch_size대로 in쿼리 개수가 안 나가는 이유!
JPA를 사용할 때 N+1 문제를 해결하기 위해 default_batch_fetch_size를 설정하여 in절로 여러 개를 한 번에 가져올 수 있도록 한다.
하지만 실제 default_batch_fetch_size를 100으로 설정하고 30개의 데이터를 가져오려 하니 25개를 담은 in절 쿼리 1번 + 5개를 담은 in절 쿼리 한 번이 호출되었다.
Statement와 PreparedStatement
RDB의 경우 쿼리가 호출 될 때 아래의 3가지의 과정을 거친다.
1) 쿼리 문장 분석
2) 컴파일
3) 실행
Statement를 사용할 경우 매번 쿼리가 수행할 때마다 위의 3단계를 거치게 되고, PreparedStatement는 처음 한 번만 세 단 계를 거친 후 캐시에 담아 이후부턴 재사용을 한다. 그러므로 만약 동일한 쿼리를 반복적으로 수행한다면 PreparedStatment가 DB에 훨씬 적은 부하를 주며, 성능도 좋을 것이다.
default_batch_fetch_size 옵션
보통 RDB들은 select * from x where in (?) 와 같은 preparedstatement는 미리 문법을 파싱해서 최대한 캐싱을 해둔다.
그런데 데이터가 1개, 2개, 3개, 100개가 있으면 모두 각각 다음 처럼 최대 100개의 preparedstatement 쿼리를 만들어야 한다.
select * from x where in (?)
select * from x where in (?, ?)
select * from x where in (?, ?, ?)
select * from x where in (?, ?, ? ...)
이렇게 되면 DB 입장에서 너무 많은 preparedstatement 쿼리를 캐싱해야 하고, 성능도 떨어지게 된다.
그래서 하이버네이트는 이 문제를 해결하기 위해 내부에서 나름 최적화를 한다. (규칙을 정하는 것)
default_batch_fetch_size: 100 (설정값)
50 = 100/2
25 = 50/2
12 = 25/2
10 = 10
9 = 9
...
1 = 1
그리고 1~10까지는 자주 사용하니 모두 설정. 이런식으로 잡아둔다.
그러면 기존에 100개의 preparedstatement 모양을 1~10, 12, 25, 50 ,100 해서 총 14개의 모양으로 최적화를 한다.
이렇게 해서 100으로 최대값을 설정하고, 18을 설정하면 12, 6 같이 나누어서 실행된다.
참고자료
https://jeonyoungho.github.io/posts/default_batch_fetch_size/
[JPA] default_batch_fetch_size
JPA를 사용할 때 N+1 문제를 해결하기 위해 default_batch_fetch_size를 설정하여 in절로 여러 개를 한 번에 가져올 수 있도록 한다.
jeonyoungho.github.io