알고 있었다고 생각했지만 명확히 설명하지 못하는 모습에

확실하게 알고 넘어가기 위해 글을 작성하기로 마음 먹었다

 

1. N+1 문제란 무엇인가?

- 1번 조회되어야 할것이 N개의 종류의 데이터 각각을 추가로 조회해서

총 N+1번 DB 조회를 하게되는 문제라고 한다

 

1) 언제 발생하는가?

- JPA Repository를 활용해 인터페이스 메서드를 호출할때 (Read) 발생한다

 

2) 누가 발생하게 하는가?

- 1:N (일대다) 또는 N:1(다대일) 관계를 가진 엔티티를 조회할때 발생한다

 

3) 어떻게 발생하게 되는가?

- JPA Fetch 전략(Fetch Type)이 EAGER 전략으로 데이터를 조회하는 경우

- JPA Fetch 전략(Fetch Type)이 LAZY 전략으로, 전체 데이터를 가져온 이후

연관 관계인 하위 엔티티를 사용할 때 다시 조회하는 경우

 

4) 왜 발생하게 되는가?

- JPA Repository로 find 시 실행하는 첫 쿼리에서 하위 엔티티까지 한 번에 가져오지 않고

하위 엔티티를 사용할 때 추가로 조회하기 때문에

- JPQL은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL만 가지고 SQL을 생성하기 때문에

 

2. EAGER (즉시로딩), LAZY (지연로딩) 비교하기

1) EAGER (즉시로딩)

- JPQL에서 만든 SQL을 통해 데이터를 조회한다- 이후 JPA에서 Fetch 전략을 가지고(여기서는 즉시 로딩) 해당 데이터의 연관 관계인 하위 엔티티들을 추가 조회(LAZY - 지연 로딩 발생)- 그렇기에 N+1 문제 발생한다

 

2) LAZY (지연로딩)

- JPQL에서 만든 SQL을 통해 데이터를 조회- JPA에서 Fetch 전략을 가지지만, 여기서는 지연 로딩이기 때문에 추가 조회는 하지 않음- 하지만, 하위 엔티티를 가지고 작업하게 되면 추가 조회하기 때문에 결국 N+1 문제가 발생함

 

3. N+1 발생 직접 눈으로 확인해보기

1) Member 코드

 

2) Document 코드

 

- Document와 Member는 다대일 관계를 가지고 있다

 

테스트를 위해서 8명의 member를 만들고7개의 document를 만드는데 이는 5명의 member가 나눠 가진다

 

 

이때 document를 모두 불러와 본다그러면 첫번째로 전체 document를 불러오는 쿼리 1개랑document와 연관된 member를 각각 불러와지는 쿼리 5개가 발생한다

 

document가 7개 였는데 왜 5개냐고 한다면

아까 5명의 member가 각각 document를 가지고 있다했다

이는 마지막 banner4 라는 member가 중복으로 있기 때문이다

 

즉 root, root1, root2, root3, banner4 이렇게 5명이

document를 가지고있으니 연관된 5명만 더 쿼리로 불러와 지는 것이다

 

 

그렇다면 반대로 1:N 관계를 가진 member를 전체 조회하면 어떻게 될까

첫번째로 member 전체 불러오는 쿼리 1개를 발생되고

각 member가 가지고 있는 document를 조회하는 쿼리 5개가 발생한다

 

4. 해결방법은 무엇이 있나요?

1) 가장 대표적인 방법은 Fetch Join을 이용하기

2) Batch size 설정하기

3) @EntityGraph 사용하기