TIL

[TIL] 20240306 JPA N+1 문제

yjyj0101 2024. 3. 6. 21:54

1. N+1 문제란?

N+1 문제는 한 번의 쿼리로 N개의 연관된 엔티티를 가져올 때, 각각의 연관된 엔티티를 가져오기 위해 N번의 추가 쿼리가 실행되는 현상

 

2. 해결방법

  • Global Fetch
    • @ManyToOne 속성에 fetch 속성으로 LAZY로 설정
    • 엔티티를 생성할 때(컴파일 시점) 결정 되는 연관관계 전략임
  • Fetch Join
    • JPQL의 fetch join을 사용하면 연관된 엔티티를 한 번의 쿼리로 함께 가져올 수 있음
    • 조인할 때 연관된 엔티티나 컬렉션를 함께 조회하려고 할 때 사용하고, 결과는 EAGER 와 똑같지만 과정은 다름
List<Post> posts = entityManager.createQuery(
        "SELECT p FROM Post p JOIN FETCH p.comments", Post.class)
    .getResultList();

 

  • Entity Graph
    • @EntityGraph 어노테이션을 사용하면, 특정 엔티티를 조회할 때 어떤 연관 엔티티들을 Eager 로딩할지 세밀하게 지정가능
    • 지연 로딩으로 설정되어 있으면 연관관계에서 종속된 엔티티는 쿼리 실행 시 select 되지 않고 proxy 객체를 만들어 엔티티가 적용 ->  proxy 객체를 호출할 때마다 그때 그때 select 쿼리가 실행 되므로
    • fetch 조인을 어노테이션으로 사용할 수 있도록 만들어 준 기능임
public interface CommentRepository extends JpaRepository<Comment, Long> {
    @EntityGraph(attributePaths = {"thread"}, type = EntityGraph.EntityGraphType.LOAD)
    List<Comment> findAll();
}
EntityGraphType.LOAD EntityGraphType.FETCH  
attributePaths에 정의한 엔티티들은 EAGER, 나머지는 글로벌 패치 전략에 따 라 패치 attributePaths에 정의한 엔티티들은 EAGER, 나머지는 글로벌 패치 전략에 따 라 패치

 

 

+ 성능 최적화 추가사항 )

  • Batch Fetching
    • 연관된 엔티티를 일정 크기의 배치로 묶어서 조회
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    @BatchSize(size = 10) // 여기에 배치 사이즈를 지정
    private List<Comment> comments;
spring:
    properties:
      hibernate:
        default_batch_fetch_size: 100