[SpringBoot / JPA] JPA Batch Size에 대한 고찰

HeavyJ·2023년 5월 13일
6

자바/스프링부트

목록 보기
11/17

JPA의 N+1 문제를 해결할 수 있는 전략으로 Batch Size를 설정하여 쿼리 수를 압도적으로 줄일 수 있습니다.

@BatchSize( size = n )

기술 블로그를 서칭하면서 상황에 맞게 Batch Size를 고려해야 한다라는 글을 읽은 적이 있습니다.
저는 이 부분이 참 이해가 안 갔던게, 적절한 Batch Size를 통일해서 설정해놓으면 되는 거 아닌가?라는 생각이 있었습니다.
적당히 100개 ~ 1000개 정도?

라고 생각했었는데, 최근에 Batch Size를 설정하면서 Batch Size를 통일하는 건 좋은 생각이 아니구나를 깨달았습니다.

Batch Size를 설정하면서 발생했던 문제들에 대해서 포스팅하겠습니다.

Batch Size가 실제 Data 수 보다 클 때 문제 발생

현재 게시판과 댓글이 One : Many 관계 + 양방향 매핑관계라고 하겠습니다.

Post.class

//~~ 생략

@OneToMany(mappedBy = "article")
private List<Comment> commentList;

Comment.class

// ~~ 생략

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn("post_id")
private Post article;

이럴 경우 OneToMany에서 발생하는 N + 1 문제 해결을 할 때 Batch Size를 설정해줍니다.

BatchSize는 타 블로그에서 100~1000 정도를 설정하는게 적절하다고 해서 100으로 설정해줍니다.

Post.class

@BatchSize(size = 100)
@OneToMany(mappedBy = "article")
private List<Comment>

현재 제 Post 테이블에 데이터가 20개 들어가 있으니까 대충 100개 정도로 설정해놓으면 한 번에 20개를 in 연산자로 가져오겠거니 했습니다.

그런데 쿼리가 2개로 나눠져서 발생했고 추가로 가져오니 in 데이터의 개수도 이상했습니다.

첫 번째 쿼리에서는 12개의 데이터를 가져오고 두 번째 쿼리에서는 8개의 데이터를 가져왔습니다.

문제

  • 뜬금없는 in 연산자로 가져오는 데이터 개수 12...
  • 쿼리 2개 발생

가져오는 개수가 Batch Size(100)과 전혀 연관성이 없을뿐더러, 왜 쿼리가 2개로 나눠서 나오지라는 궁금증이 생겼습니다.


Batch Size 줄여보기

해결을 위해서 배치 사이즈를 조금씩 줄여보기로 했습니다. 배치 사이즈를 50개로 줄여봤습니다만, 50개로 줄여도 같은 결과가 나왔습니다😂

30개로 줄여봐야겠다 하고 줄였는데

이번에는 15개, 5개로 쿼리가 나눠서 발생했습니다.

(왜 숫자가 또 바뀌냐고ㅜㅜ ㅋㅋㅋㅋ)

그러던 중, 어 30? -> 15?
2배수? 라는 규칙을 찾았습니다.

잠시만, 그러면 40을 Batch Size로 설정하면 절반인 20개 다 온전히 가져오나? 라는 생각으로 size를 40으로 변경해봤습니다.

캬.. 성공

40으로 size를 변경하니 20개의 데이터를 온전히 가져오고 쿼리도 1개만 발생하는 것을 확인할 수 있었습니다.

그러면 100으로 했을 때는 왜 12 + 8로 나오는거지.. ?
설마 100 -> 50 -> 25 -> 12.5 = 12 이런건가?

20보다 작은 값으로 가져와야 하기 때문에 12가 채택이 됐고 남은 8개의 쿼리를 추가로 가져오는 것입니다.

그러면 배치 사이즈의 절반씩 계속 나눈 뒤 해당 데이터의 수와 근접한 개수의 쿼리가 나간다는 가설을 세울 수 있습니다.

실제로 이 부분이 맞는지 궁금해서 구글링을 했는데, Batch Size 캐싱 전략에 관한 정보를 확인할 수 있었습니다.

원인은 Batch Size 캐싱 케이스 최적화

https://42class.com/dev/jpa-batchsize/

이 블로그를 참고하자면, Batch Size를 관리하는 Hibernate가 Batch Size를 캐싱하는 방식 때문에 이렇게 쿼리가 나눠서 발생하는 것입니다.

최적화를 위해서 캐싱케이스를 줄여야 하는데 프로젝트에 선언된 배치 사이즈를 기준으로 절반식 나눠가면서 캐싱을 하는 것입니다.
즉, in절 항목값을 100으로 잡은 경우 최적화를 안 하면 100개의 케이스를 캐싱해야 합니다.
(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,....99,,100)

하지만, 최적화를 하게 되면
(1,2,3,4,5,6,7,8,9,10,12,25,50,100) 이렇게 14개의 케이스만 있으면 됩니다.

1,2,3,4,5,6,7,8,9,10은 기본적으로 모두 캐싱하기 때문에 Batch Size가 10보다 클 경우 1~10은 항상 캐싱 케이스에 포함된다고 합니다.

결국 12라는 뜬금없는 숫자는 이렇게 절반씩 줄여가는 캐싱 케이스 때문에 발생했던 겁니다.

만약에 배치 사이즈를 30으로 잡았다면 캐싱 케이스는
(1,2,3,4,5,6,7,8,9,10,15,30)
이렇게 12개의 캐싱 케이스가 발생합니다.

확실히 이 전략이 좋은게 최적화를 하지 않으면 30과 100의 캐싱 케이스 개수의 차이가 70개인데, 최적화를 하니까 차이가 2개밖에 발생을 안 하네요

그러면 여기서 고려해야 하는 것이 있습니다.

Batch Size를 실제 Data Size 보다 크게 하는 것이 좋은가 ? 작게 하는 것이 좋은가 ? 를 생각해볼수 있습니다.

적절한 배치 사이즈 설정 전략

(여기서 부터는 제 뇌피셜입니다.. ㅎㅎ 😆)
제 생각에는 데이터의 개수가 1000개가 넘어가는 경우에는 기존의 적절하다거 알려진 Batch Size(100 ~ 1000) 정도로 설정하면 될 것 같습니다.

반대로, 데이터의 개수가 작은 편일 경우, Batch Size는 절반보다 약간 큰정도로 즉, 실제 Data Size보다 작게 설정하면 될 것 같네요.
왜냐하면, Batch Size가 100이고 실제 Data 개수가 99개 일 때는 (50 + 25 + 12 + 10 + 3) 이렇게 5개의 쿼리가 발생하는 반면, Batch Size를 50으로만 해도 (50 + 49) 2개의 쿼리로 줄일 수 있습니다.

물론, 금방 금방 데이터가 채워지는 경우라면 Batch Size를 크게 설정하는게 좋겠지만, 데이터 변동이 적은 테이블의 경우 Batch Size를 데이터 개수보다 작게 설정하는 것도 생각해볼 수 있겠습니다.

이번 경험을 통해서 왜 상황에 따라 배치 사이즈를 다르게 설정해야 하는지에 대해 알게 됐습니다! 역시, 상황에 따라.. 다르게.. (이게 젤 어렵네요..ㅎㅎ)

쿼리의 개수를 더 많이 줄일 수 있는 방법에 대해서 고민을 더 많이 해봐야겠네요!

profile
There are no two words in the English language more harmful than “good job”.

8개의 댓글

comment-user-thumbnail
2023년 5월 15일

오.. 배치사이즈 설정에 따라서 캐싱케이스가 저렇게 나뉘는 것은 처음 알았네요!! 😮 다음번엔 무조건 크게 잡지 않고, 데이터 불러오는 개수를 생각하면서 최적화 해봐야겠습니다~ 좋은 글이네요

1개의 답글
comment-user-thumbnail
2023년 5월 16일

오랜만에 정말 도움이 되는 글입니다! 저도 이점을 활용해서 코드를 작성하고 블로깅 해봐야겠어요

1개의 답글
comment-user-thumbnail
2023년 8월 28일

"왜냐하면, Batch Size가 100이고 실제 Data 개수가 99개 일 때는 (50 + 25 + 12 + 10 + 3) 이렇게 5개의 쿼리가 발생하는 반면, Batch Size를 50으로만 해도 (50 + 49) 2개의 쿼리로 줄일 수 있습니다."

후자의 경우 똑같이 (50, 25, 12, 10, 2)로 쿼리가 발생할텐데 아닌가요?
(일단 저의 경우 50개 데이터 조회시 batch size = 26일때 26, 13, 10, 1개로 총 4개의 쿼리가 작성되었습니다.)
제가 맞다면 본문의 배치 사이즈 설정 전략을 달리 짜야할 것 같습니다.

답글 달기
comment-user-thumbnail
2023년 8월 30일

약간 소름

답글 달기
comment-user-thumbnail
2023년 11월 27일

우아,.. 도움받고갑니다 !!! 좋은글 감사해요~~

답글 달기
comment-user-thumbnail
2024년 2월 18일

캐싱을 한다는 것이 무엇을 캐싱하는지 혹시 아실까요? 쿼리를 캐싱하는 것인지, 쿼리의 결과를 캐싱하는 것인지 궁금하네요.

답글 달기