Notice
Recent Posts
Recent Comments
Link
250x250
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

거인의 코딩일지

[JPA] N+1 문제?? 본문

코딩/JAVA

[JPA] N+1 문제??

코딩거인 2023. 8. 31. 15:34
728x90

JPA의 N+1 문제란??

 

  • 한번 조회해야 할 것을 N개의 종류의 데이터 각각을 추가로 조회하게 되서 총 N+1 번 DB를 조회하는 문제!
언제 발생하는가 ?
  • JPA Repository 를 활용해 인테페이스 메소드를 호출할 시!
누가 발생시키는가 ?
  • 1:N 또는 N:1 관계를 가진 엔티티를 조회할 때!
어떻게 발생하는가 ?
  • JPA Fetch 전략(Fetch type) 이 EAGER 전략으로 데이터를 조회하는 경우
  • JPA Fetch 전략(Fetch type) 이 LAZY 전략으로 전체 데이터를 가져온 이후 연관관계인 하위 엔티티를 사용할때 다시 조회하는 경우
왜 발생하는가?
  • JPA Repository 로 find 할 때 실행하는 첫 쿼리에서 하위 엔티티까지 한번에 가져오지 않고 하위텐티티를 사용할 때 추가로 조회하기 때문이다
  • JPQL 은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL 만 가지고 SQL 을 생성하기 때문이다.
EAGER(즉시로딩) 인 경우
  • JPQL에서 만든 SQL 을 통해 데이터를 조회
  • 이후 JPA에서 Fetch 전략을 가지고 (이땐 즉시로딩) 해당 데이터의 연관관계인 하위 엔티티들을 추가 조회(LAZY : 지연로딩)과정에서 N+1 문제가 발생한다.
LAZY(지연로딩)인 경우
  • JPQL에서 만든 SQL 을 통해 데이터를 조회
  • JPA 에서 Fetch 전략을 가지지만 여기서는 지연로딩이기에  추가조회는 하지 않는다
  • 하지만 하위엔티티를 가지고 작업하게 되면 추가 조회하기에 결국 N+1  문제가 발생한다.
@ManyToOne 의 기본 FetchType 은 EAGER 이지만 특별한 경우가 아니라면 LAZY를 기본 세팅해서 사용하는 것이 옳다.
LAZY 를 사용하더라도 N+1 이 발생할수 있음을 인지하고 사용하여야 한다.
N+1 해결방법???
Fetch Join, BatchSize, EntityGraph 어노테이션 등의 방법이 있다.
  1. Fetch Join 
    • DB에서 데이터를 가져올 때 처음부터 연관된 데이터 까지 같이 가져오게 하는 것(SQL Join 문)
    • 별도의 메소드를 만들어 줘야하며 @Query 어노테이션을 사용하여 "join fetch 엔티티. 연관관계 _ 엔티티" 구문을 만들어 주면 된다.
    • 장점
      • 단 한번의 쿼리만 발생하도록 설계할수있다.
      • fetch join 을 이용해 특정 엔티티의 하위엔티티의 하위엔티티 까지 가져오도록 할 수 있다.
    • 단점
      • 번거롭게 쿼리문을 작성해야한다.
      • JPA가 제공하는 Pageable 기능을 사용하지 못한다 => 페이징 단위로 데이터 가져오기 불가능
        • batchsize 로 해결 : 즉시로딩이나 지연로딩시에 연관된 엔티티를 조회할 때 지정한 size 만큼 sql 의 IN 절을 사용하여 조회하는 방식
      • 1: N 관계가 2개인 엔티티를 fetch join 불가능 => MulitipleVagfetch Exception 발생
  2. Batch Size
    • 이 옵션은 정확히는 N+1 문제를 안일어나게 하는 방법은 아니고 N+1 문제가 발생하더라도 "Select * from user where team_id = ?"  가 아닌 "Select * from user where team_id in (?,?,?,?)" 방식으로 N+1 문제가 발생하게 하는 방법
  3. EntityGraph 어노테이션
    • EntityGraph 상에 있는 Entity 들의 연관관계 속에서 필요한 엔티티와 컬렉션을 함께 조회하려고 할 때 사용
    • outerJoin 사용
    • attribute Paths에 쿼리수행시 바로 가져올 필드명을 지정하면  LAZY(지연로딩)가 아닌 Eager(즉시로딩) 조회로 가져오게된다.
    • 장점 
      • fatch join의 매번 쿼리를 작성하고 확인하느 문제 수행
    • 단점
      • outerjoin 을 사용하기 때문에 중복데이터가 발생한다 (카테시안 곱 현상)
        • Set 으로 중복 방지 / distinct 적용!

 

728x90