본문으로 바로가기

[JPA] N+1 문제

category TIL/JPA 2022. 4. 19. 16:18

N+1 문제

FetchType.EAGER를 필요한 곳에서 Lazy Loading으로 쿼리가 실행될 때 발생하는 문제

 

 ↓ Lazy Loading vs Eager Loading

더보기

Lazy Loading (지연된 로딩)

: 필요 시점까지 객체의 초기화를 연기시키기 위해 컴퓨터 프로그래밍에 흔히 사용되는 디자인 패턴

  • 페이지를 불러올 때 당장 필요하지 않은 리소스들을 나중에 로딩
    (사용자가 보지 않는 것들은 나중에 로딩)
  • Lazy Loading으로 설정되어 있는 엔티티는 프록시 객체로 불러와짐
  • 사용자가 필요로 하는 시점에 로딩
  • 콘텐츠의 제공 속도가 빠름
  • (예시) 무한 스크롤 사용 시 - 사용자의 스크롤 깊이를 계산하다가 페이지 끝에 도달했을 때 데이터를 요청

 

Eager Loading (즉시 로딩)

: 조인을 사용하여 SQL을 한번에 조회

  • select 쿼리를 여러번 수행해서 엔티티 조회
  • join 사용
  • 네트워크 콘텐츠가 접근되고 초기화 시간을 최소로 유지해야 하는 상황에서 이상적

 

발생 상황

1. Join Fetch 사용

@Query("select u from User u join fetch u.chat")
@Query("select u from User u join fetch u.chat c join fetch c.title")
  • inner join 발생
  • 불필요한 쿼리문이 추가됨

 

2. Entity Graph

@EntityGraph(attributePaths = "chat")
@Query("select u from User u")
List<User> findAllEntityGraph();
@EntityGraph(attributePaths = {"chat", "chat.title"})
@Query("select u from User u")
List<User> findAllEntityGraphWithTitle();
  • attributePaths에 쿼리를 수행할 때 바로 가져올 필드명일 지정하면 Eager 로딩으로 조회

 

해결 방안

1. Set 필드 타입 선언

  • 일대다 필드의 타입을 Set으로 선언 → 중복 방지
  • 순서를 보장하려면 LinkedHashSet 사용
@OnteToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_seq")
private Set<Chat> chats = new LinkedHashSet<>();

2. distinct 사용

@Query("select DISTINCT u from User u join fetch u.chat c join fetch c.title")

 

 


[ 출처 ]

https://ko.wikipedia.org/wiki/%EC%A7%80%EC%97%B0%EB%90%9C_%EB%A1%9C%EB%94%A9

https://velog.io/@vagabondms/%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%84%B0%EB%94%94-Lazy-loading%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80

https://jojoldu.tistory.com/165

'TIL > JPA' 카테고리의 다른 글

[JPA] 데이터베이스 스키마 자동 생성하는 방법  (0) 2022.05.07
[JPA] annotation 종류  (0) 2022.05.06
[JPA] Querydsl 설정  (0) 2022.04.19
[JPA] JPA란?  (0) 2022.04.19