본문 바로가기
  • 시 쓰는 개발자
Spring

JPA N+1 문제

by poetDeveloper 2024. 5. 5.

여러 인터넷 내용들을 참고하여 정리하였습니다.

[개발면접3분] N+1 문제와 해결방법
https://www.youtube.com/watch?v=B0iYvMJ6eG4  

[10분 테코톡] 수달의 JPA N+1 문제
https://www.youtube.com/watch?v=ni92wUkAmQI

N+1 문제 : ORM 사용시 발생하는 쿼리 문제. 1개의 쿼리문에 N개의 추가 쿼리문이 요청되어 성능이 저하되는 것을 의미한다. 즉, 1+N 문제이다.

OneToMany 예시 (게시글-댓글 상황)

>> 1단계
게시글 - 댓글처럼 OneToMany 관계에서 Lazy 로딩을 적용한 상황을 생각해보자. 그럼 게시글을 조회했을 때, 아직 댓글은 안받아오니까 댓글을 proxy 객체로 가지고 있다. 근데 만약 이 상황에서 댓글을 조회한다면 어떻게 될까 ??

 

>> 2단계
프록시 객체이기 때문에, JPA는 댓글 데이터가 캐싱되어있는지 확인한다. 근데 없으니까 쿼리를 만들어서 댓글 데이터를 조회한다. select * from 댓글 where 게시글ID=1;  

 

>> 3단계
그런데 첫번째 게시글 뿐만 아니라, 모든 게시글에 대해서 댓글은 Lazy이기 때문에 프록시로 가지고 있어서 2단계와 같은 상황이므로 쿼리를 계속 보내게 된다. select * from 댓글 where 게시글ID=2, 3, 4, 5,(반복).... 따라서 이런 상황이 N개의 게시글에 대해서 반복되므로 쿼리가 굉장히 많이 보내지는 문제가 발생하게 된다.

해결법

  • 지연로딩 + Fetch join : 연관된 엔티티나 컬렉션을 한번에 같이 조회하는 기능이다. 코드로 쓴다면 select 게시글.*, 댓글.* from 게시글 join fetch 댓글 이라고 써준다 .처음에 관련 데이터들을 다 같이 가져와서 객체화 시켜주기 때문에, DB 조회 없이 바로 데이터를 반환할 수 있다. 이렇게 되면 조회시 댓글도 프록시 객체가 아니라 진짜 댓글객체가 만들어지게 된다.
  • Batch Loading (일괄 조회) : 우선적으로 필요한 데이터(게시글)를 조회하고, 연관된 데이터에 대해서는 해당 엔티티의 ID를 모두 모아서 한번에 쿼리를 요청. 게시글1의 댓글 쿼리 요청, 게시글2의 댓글 쿼리 요청 , 게시글3의 댓글 쿼리 요청 .... 이렇게 하는 게 아니고 N개의 게시글에 대해 댓글ID를 미리 모아두고 한번에 쿼리를 보내기 때문에 N번의 쿼리를 요청하는 문제를 해결하는 방법이다.

Q. 즉시 조회로( Eager Loading ) 하면 되는 것 아닌가요 ??

>> Eager Loading (즉시 조회) : 모든 게시물 받아오면서 그때 댓글까지 한번에 불러옴. 즉, 연관된 모든 데이터를 한번에 받아오겠다는 것. 하지만, 당장은 필요없는 데이터까지 받아오기 때문에 over-fetching 문제가 발생할 수 있음. 이로 인해 로딩 시간이 증가되어 성능저하 발생 가능함.
 그런데, 즉시 로딩에서도 N+1 문제가 생길 수 있다!! 이는 JPQL이 쿼리를 만드는 과정을 알아야한다. JPQL은 처음 쿼리를 만들 때 연관관계 신경 안쓰고, 조회 대상 엔티티 기준으로 쿼리를 만들게 된다. 즉, 게시글을 먼저 조회한 다음에, 그 후에야 연관관계를 확인한 후 FetchType.EAGER를 확인하고서 댓글을 가져오게 된다. 따라서 이때도 N+1 문제가 생기게 된다.

'Spring' 카테고리의 다른 글

스프링 느낀점 - 2023.03.07. 작성  (0) 2024.05.09
annotation 정리  (0) 2024.03.11
Controller vs RestController  (0) 2024.03.09
RequestBody, ResponseBody  (0) 2024.03.08
Bean에 대하여  (0) 2024.03.08