[Study] N+1 문제 및 해결 방법

2023. 5. 1. 15:42· Study/Concept
목차
  1. 들어가기 앞서.. 
  2. N+1 ?
  3. 해당 N+1 문제는 언제 발생하고, 어떠한 상황에 발생하는가?
  4. N+1 문제는 어떻게 해결해야 하는가?
728x90

 

들어가기 앞서.. 

스터디를 진행하다, N+1 문제를 직면하게 되었다. Pet과 Visit 쪽에서 조회 쿼리를 날릴 시, 레코드 개수만큼 쿼리문이 더 날라가는 것을 확인했다. 앞서 김영한님의 ORM JPA 강의와 다른 코딩 책을 공부하며 접한적이 있지만, 다시 한번 제대로 정리해보는 시간을 가져보려 한다.

공부하기 앞서, 내가 알던 N+1 문제는 쿼리 실행 시, 개발자의 의도와 다르게 쿼리문이 N 개가 더 나가는 것으로 알고있다. 이번기회로 다시 정리해보겠다.

 


 

N+1 ?

앞서 설명했듯 간단하게 설명하자면, 연관 관계에서 발생하는 문제로, 연관관계가 설정된 엔티티를 조회 시 조회된 데이터의 개수(N) 만큼 연관관계 조회 쿼리가 추가로 발생하는 것이다. 이것을 N+1 문제라고 한다. 

이러한 문제를 가볍게 여기고 넘어가면 추후에 문제가 발생할 수 있다. 현재 데이터가 적어서 몇개의 쿼리가 나가지 않는다고 안심하여도, 추후 데이터가 수백 수천개가 쌓였을 때, 해당 N+1 문제가 발생하는 조회 쿼리를 실행하게 된다면 수백, 수천개의 쿼리가 나가게 되며 성능이 심각하게 저하될 것이다. 

 


 

해당 N+1 문제는 언제 발생하고, 어떠한 상황에 발생하는가?

1:N, N:1 과 같은 연관관계를 가진 엔티티를 조회할 때 발생한다.

JPA fetch 전략이 EAGER, LAZY에 따라 언제 발생하는지가 조금 다르다.
EAGER인 경우에는 데이터를 조회하는 것마다 발생하게 되고, LAZY의 경우에는 해당 엔티티만 조회하게 되면 문제가 발생하지 않지만, 하위 엔티티를 같이 조회하게 될 경우 발생하게 된다. 조금 더 자세하게 설명하면 다음과 같다.

 

EAGER의 경우

  1. JPQL에서 만든 SQL을 통해 데이터 조회
  2. JPA에서 Fetch 전략이 EAGER이기에, 즉시로딩으로 하위에 있는 엔티티까지 추가적으로 조회
  3. 그렇기에 N+1 문제 발생

LAZY의 경우

  1. JPQL에서 만든 SQL을 통해 데이터 조회
  2. JPA에서 Fetch 전략이 LAZY이기에, 지연로딩으로 하위에 있는 엔티티를 직접 작업하는 것이 아니라면 조회X
  3. 하지만 하위 엔티티를 직접 작업하게 되면, 추가 조회를 해야하기에 N+1 문제 발생

 


 

N+1 문제는 어떻게 해결해야 하는가?

크게 해결 방법에는 FetchJoin, EntityGraph, BatchSize가 있다. 이번에는 FetchJoin과 EntityGraph를 한번 알아보겠다.
우선 N+1 문제가 발생하는 예시를 먼저 보겠다. 

 

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Setter
@Entity
public class Owner {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private String name;
 
    @OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
    private List<Pet> pets = new ArrayList<>();
}
 
 
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Getter
@Entity
public class Pet {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    private String name;
 
    @ManyToOne
    private Owner owner;
}
    @Test
    void test() {
        List<Pet> pets = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Pet pet = Pet.builder().name("pet" + i).build();
            pets.add(pet);
        }
        petRepository.saveAll(pets);
 
        List<Owner> owners = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Owner owner = Owner.builder().name("owner" + i).build();
            owner.setPets(pets);
            owners.add(owner);
        }
        ownerRepository.saveAll(owners);
 
        System.out.println("-------------------------------");
        List<Owner> ownerList = ownerRepository.findAll();
    }

 


 

FetchJoin

위에서 N+1 문제가 발생하는 이유는, 한쪽 테이블만 조회하고 그에 연관된 다른 테이블은 따로 조회하기에 발생하는 것이다. 두 테이블을 Join을 사용하여 한번에 모든 데이터를 조회한다면 N+1 문제는 발생하지 않을 것이다.

이를 해결하기위해서 FetchJoin을 사용하는 것이다.

@Query("select o "
    + "from Owner o "
    + "join fetch o.pets")
List<Owner> findAllJoinFetch();

위와 같이 작성해준 후, 실행해보면 앞서와 다르게 쿼리가 Join 문으로 하나만 나가는 것을 확인할 수 있다.

 

@EntityGraph

@EntityGraph의 경우는 attributePaths에 같이 조회할 연관 엔티티명을 작성해주면 된다. 여러개를 적어줄 수 있다.

@EntityGraph(attributePaths = {"pets"})
@Query("select o from Owner o")
List<Owner> findAllEntityGraph();

 

Fetch Join과 다른점이 있다면, FetchJoin은 inner join하지만, EntityGraph는 outer join 한다. 성능 최적화상 inner join이 더 효울적이다. 

 

 


 

728x90
저작자표시 (새창열림)
  1. 들어가기 앞서.. 
  2. N+1 ?
  3. 해당 N+1 문제는 언제 발생하고, 어떠한 상황에 발생하는가?
  4. N+1 문제는 어떻게 해결해야 하는가?
'Study/Concept' 카테고리의 다른 글
  • [Study] AOP ? (정의 + 용어 + 적용 방식)
  • [Study] QueryDSL - 동적 쿼리(Dynamic SQL) 사용
  • [Study] QueryDSL 이란?
  • [Study] ResponseEntity 란 무엇인가?
soohykeee
soohykeee
Computer Science. 2017~2023 / Java, Spring, Backend
soohykeee
Coding_
soohykeee
전체
오늘
어제
  • 분류 전체보기
    • 회고
    • Info
      • 개념 정리
      • 정보
    • Study
      • Pet-Clinic-Project
      • Concept
    • Inflearn
      • 스프링 핵심 원리_기본편
      • Git
    • 코드로 배우는 스프링 부트 웹 프로젝트
      • Guestbook
      • MovieReview
      • Security & API

블로그 메뉴

  • 홈
  • 방명록
  • Github

인기 글

최근 댓글

최근 글

250x250
hELLO · Designed By 정상우.v4.2.1
soohykeee
[Study] N+1 문제 및 해결 방법
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.