Kim-Baek 개발자 이야기

JPA Fetch 전략과 N+1 문제 본문

개발

JPA Fetch 전략과 N+1 문제

김백개발자 2020. 8. 5. 15:50

Fetch Type 이란

Fetch Type 은 JPA 가 하나의 Entity 를 조회할 때, 연관관계에 있는 객체들을 어떻게 가져올 것이냐를 나타내는 설정값입니다.

Fetch Type 은 크게 Eager 와 Lazy 두가지 전략이 있습니다. Fetch Type Issue 상황이라는 것은 하나의 Entity 를 로드할 때, 아래의 두가지 전략 중 고민하는 상황을 말합니다.

  • 연관 관계에 있는 Entity 들 모두 가져온다 → Eager 전략
  • 연관 관계에 있는 Entity 가져오지 않고, getter 로 접근할 때 가져온다 → Lazy 전략

N+1 문제는 이럴 때 발생합니다.

ManyToOne, OneToOne 컬럼의 FetchType 을 LAZY 로 하였을 경우 발생합니다.

이렇게 하위 엔티티들을 첫 쿼리 실행시 한번에 가져오지 않고, Lazy Loading으로 필요한 곳에서 사용되어 쿼리가 실행될때 발생하는 문제가 N+1 쿼리 문제입니다.

언제 발생하는가?

다음의 예제 엔티티 클래스들과 함께 설명을 진행해보겠다. 

마스터와 학생은 일대다 관계를 갖고 있다. 한 마스터가 여러명의 학생을 관리하고 있고, 양방향 연관관계가 수립되어 있다. 

@Entity
public class Master {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany
    private List<Student> students = Lists.newArrayList();

    ...
}

@Entity
public class Student {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    private Master master;

    ...
}

아래는 마스터들을 조회할 수 있는 레포지토리 인터페이스이다. 

public interface MasterRepository extends JpaRepository<Long, Master> {

    (JPA 레포지토리가 기본 제공해주는 메소드들이 당연히 포함되어 있다.)

}

이 상황에서 MasterRepository.findAll() 을 실행했을 때, 우리는 이런 쿼리가 날라가길 기대할 것이다.

SELECT * FROM MASTER
LEFT JOIN STUDENT
ON STUDENT.MASTER_ID = MASTER.ID

현실은…..

SELECT * FROM MASTER

SELECT * FROM STUDENT WHERE MASTER_ID = 0
SELECT * FROM STUDENT WHERE MASTER_ID = 1
SELECT * FROM STUDENT WHERE MASTER_ID = 2
SELECT * FROM STUDENT WHERE MASTER_ID = 3

...

DB에 상당한 부하를 일으키게 된다.

 

 

 

1. Join Fetch

1번째는 join fetch를 사용하는 것입니다.

2. @EntityGraph

2번째 방법은 @EntityGraph을 사용하는 것입니다.

oinFetch는 Inner Join, Entity Graph는 Outer Join이라는 차이점이 있음을 주의해주세요.

공통적으로 카테시안 곱(Cartesian Product)이 발생하야 중복이 발생한다.

해결하기 위해서, set을 쓰거나 쿼리에 distinct 쓰자

 

글로벌 페치 전략 기본값

  • @OneToOne, @ManyToOne : EAGER
  • @OneToMany, @ManyToMany : LAZY

즉시 로딩이 필요하지 않은 @OneToOne, @ManyToOne 연관관계에 대해서는 글로벌 페치 전략을 LAZY로 변경해서 불필요한 쿼리 실행 방지 

반응형
Comments