Skip to content

Latest commit

 

History

History
84 lines (54 loc) · 3.39 KB

N+1 문제 해결 방법.md

File metadata and controls

84 lines (54 loc) · 3.39 KB

학습목표

  • N+1 문제가 무엇인지 안다.
  • 그것을 해결하기 위한 방법이 어떤것이 있으면 각 방법에 장단점을 안다.
  • 각 해결하기 위한 방법들을 어떨때 사용해야 하는지 명확히 알고 실무에 적용 할 줄 안다.

N + 1 문제

  • 영속성 컨텍스트에 지연로딩(Lazy Loading) 때문에 발생하는 현상이다.
  • A와 B테이블이 서로 1 to n 으로 연관관계가 있다고 하자 나는 한번에 데이터를 보려고 한건데 일단 A테이블만 쿼리를 날리고 연관된 B테이블 쿼리를 또 날리는 것이다. 문제는 데이터가 100개가 있다고 했을때 B테이블에 날라가는 쿼리가 100개가 날라가는게 문제이다.

해결방법 1 fetch join

TODO 내가 작성한 쿼리로 대체하기

@Query("select a from Academy a join fetch a.subjects")
List<Academy> findAllJoinFetch();

한번에 가져오기 위해 JPA가 쿼리를 만들어서 가져오게 한다.

fetch join에 문제점 - 불필요한 쿼리를 발생시킨다.

TODO fetch join에 한계를 보여 줄 수 있는 코드 작성

해결방법 2 @EntityGraph

내가 Eager로 가져오고 싶은것만 가져 올 수 있다.

TODO 내가 작성한 쿼리로 대체하기

@EntityGraph(attributePaths = {"subjects", "subjects.teacher"})
@Query("select a from Academy a")
List<Academy> 

@EntityGraph 사용 주의사항 - OuterJoin 그리고 중복

TODO 중복 되는 코드 보여주기

해결방법 1 Set, LinkedSet

TODO 해당 코드 작성

하지만 어쨌든 불필요한 쿼리

해당 방법은 Java에서 중복을 제거한거고 DB에 불필요한 쿼리를 날린건 똑같다.
애초에 OuterJoin을 안쓰도록 노력하는게 좋을것 같다.

Fetch Join 과 @EntityGraph의 차이점

TODO 차이점 쿼리 넣기

Fetch Join은 InnerJoin @EntithGraph는 OuterJoin이다.

즉시로딩 (EAGER)

  • @ManyToOne 에서 기본값
  • 연관된 엔티티에 모든 데이터를 DB 콜하여 한꺼번에 가져온다.

지연로딩 (LAZY)

  • A 엔티티에 연관된 B 엔티티 정보가 있을때 A 엔티티 정보만 조회 하면 비효율적이므로 로딩을 지연하여 실제 사용할 때 데이터를 조회 한다.
  • 실제 사용 할때는 B 엔티티 정보를 사용하고자 할 때 실제 쿼리가 DB에 날리게 된다.
  • A 엔티티에서 연관된 B 엔티티 정보는 Proxy 형태로 (디버그로 보면 jvst 문자가 포함됨) 가지고 있다가 실제로 B 엔티티를 사용 할 때 쿼리를 날린다.

연관 관계의 엔티티를 어떻게 가져올 것인가

  • @OneToMany 기본값 Lazy
  • @ManyToMany 기본값 Eager

Post, Comment 테이블 관계에서 Post에서 Comments데이터를 기본적으로 가져오지 않는다.
왜냐하면 Comment에 Row가 얼마나 있을지 모르기 때문이다. 그래서 OneToMany 기본값은 Lazy 이다.

즉, Post에 있는 데이터만 가져오겠다는 뜻이다.

프록시

  • 엔티티를 조회할 때 영속성 컨텍스트에 존재하지 않으면 DB콜을 하게 되는데 그런 I/O 발생없이 실제 사용하는 시점까지 미루고 싶을때 사용한다.
  • 즉, 지연로딩을 사용하기 위해 프록시 객체가 필요함

Memeber member = entityManager.getReference(Member.class, "member1");

참고