미소를뿌리는감자의 코딩

[Spring] JPA n+1 문제 본문

강의수강/[Spring]

[Spring] JPA n+1 문제

미뿌감 2024. 3. 20. 14:52
728x90

아직 내가 작성하는 코드에서 n+1 문제가 발생하진 않았지만, 흔하게 일어나는 문제인 만큼

이에 대해서 깊게 알아보고자 한다.

 

https://jh2021.tistory.com/21

 

JPA n+1 문제는 왜 생기는걸까?

Java ORM 기술인 JPA를 사용하다보면 필수적으로 n+1문제를 맞닫뜨리게 됩니다. 내가 의도 하지않는 쿼리가 나가고 거기다 여러번 나간다면 정말 무섭겠죠. 왜 발생하고 어떻게 해결해 나가야 정리

jh2021.tistory.com

 

https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1#entitygraph

 

N+1 문제 | Incheol's TECH BLOG

JPA N+1 문제에 대해 알아보자

incheol-jung.gitbook.io

이분의 글을 보고 더 깊게 알아보았다. (감사합니다 (_ _) ) 

 

n+1의 주된 문제점은 쿼리가 여러번 날라간다는 점이다.

 

한 명의 집사가 10마리의 고양이를 키우고 이것이 @ManyToOne으로 묶게 된다면,

집사를 조회했을 때 연결 지어져 있는 10마리의 고양이를 조회하기 위해 집사의 수 만큼의 쿼리가 더 추가적으로 발생하게 된다. 

 

owner가 한번 call하게 되면, owner와 연결된 cat을 부르기 위한 쿼리가 너무 많이 불러지게 된다.

 

이를 해결하기 위한 방법은 다음과 같다.

 

1. 즉시로딩

- Join Fetch

- @EntityGraph 

 

2. 지연 로딩

- BatchSize : 연관된 엔티티를 접근할 떄 지정된 수만큼 엔티티를 한번의 쿼리로 불러올 수 있다.

 

* 단순히 FetchType.LAZY 로 한다고 해서 해결이 되지는 않는다. 고양이에 접근할 때, 10개의 쿼리가 실행되는 것은 동일하다.

 

[즉시 로딩 - Join Fetch ] 

 

OwnerRepository에 다음과 같이 쿼리문을 작성해 준 후, 

 

findAllJoinFetch를 해주었다. 

하지만, assertFalse에서 error를 내뱉었고, 오랜시간 에러의 이유를 찾으려고 했지만 찾지 못했다 ㅜ

 

[즉시 로딩 - @EntityGraph ] 

 

쿼리 수행 시 바로 가져올 path를 설정하여, eager로 수행될 수 있도록 하는 방법이다.

 

이번에는 위와 같이 설정한 후, findAllJoinFetch를 해주니, 에러도 내뱉지 않고, 쿼리도 하나의 쿼리로 실행됨을 확인할 수 있었다.

 

[ 지연 로딩 - Batch Size ]

 

@BatchSize를 5로 결정한 후, findAll()을 수행한 결과는 다음과 같다.

 

 

사이즈를 설정한 만큼, in절을 묶어서 보내는 것을 의미한다.

이는 ORM 에서 성능 최적화를 위해 사용된다.

연관된 엔티티를 로딩할 때 지정된 크기의 배치로 데이터를 가지고 오는 것이다.

즉, 연관된 엔티티를 한 번에 하나씩 가져오는 대신 지정된 크기의 배치로 묶어서 가져옴으로써 데이터베이스와의 통신 횟수를 줄이고 성능을 향상시키는 기법이다.

 

batch size = 5 는 5개씩 묶어서 가져오는 것.

현재 EAGER로 설정해서, 위와 같이 5개가 묶어서 옴을 확인할 수 있다.

 

만약 .LAZY로 설정하게 된다면, 

Hibernate: select o1_0.id, o1_0.name from owner o1_0

만 출력됨을 확인할 수 있다. LAZY 이므로 사용될 때까지 아직 5개를 묶어서 가져오지 않았음을 확인할 수 있다.

 

 

< 요약 >

만약 집사 10명, 각각의 집사에 10마리의 고양이가 join되어 있다고 할 때, 

10번의 쿼리가 더 나가게 된다.

즉 10명의 집사와 연관되어 있는 무언가가 있다면, (고양이 마리의 개수는 상관이 없다) 연관된 무언가를 조회하기 위해 10번의 쿼리가 더 나가게 된다.

 

그렇다면 왜 10 + 10 이 아니라, 1+n, n+1 이라고 하는 것일까?

 

1은 초기 쿼리를 실행하는 데 필요한 쿼리 수를 의미한다.

이 초기 쿼리는 보통 어떤 엔티티의 컬렉션을 가져오기 위해 실행되는 것이므로, 1이라고 한다.

728x90