22/06/07 복잡한 쿼리의 작성과 응용

김석진·2022년 6월 7일
0

다시 초심으로

목록 보기
17/19

eager fetch, lazy fetch, N+1문제에 대해서 알아보자

먼저 Fecth란?

애플리케이션이 DB로 부터 데이터를 가져오는것
DB와 통신하여 데이터를 읽는 것에는 큰 비용이 소모되기 때문에, 똑똑하게 가져오는 전략이 필요하다

  • eager: 프로그램 코드가 쿼리를 날리는 시점에 데이터를 즉시 가져오기

    • ex: select a.id from A a inner join B b on a.b_id=b_id
      (A,B를 조인을하지만 A만봄,
      b를 보지 않았지만 일단 다 가져옴)
  • lazy: 가져오려는 데이터를 애플리케이션에서 실제로 접근할 때 가져오기

    • ex: select a.id from A; (select b from B b where b.id = ?)
    • 가져온 a.id를 이용해 b를 구하려고할때 a.id를 사용하면 비용도 적게드는 장점이 있다.
  • lazy 전략은 기본적으로 ORM의 특징이자 기능적 장점을 가지고 있다

    • 더 빠르고 경제적인 쿼리(적절히 사용한다면)
    • 잘못 사용하면 데이터 접근 에러(ex:LazyInitializationExcetpion)

    fecth 기본적략(default setting)

    각 JPA 연관관계 애노테이션은 기본 fetch 전략을 가지고이 있다.
    기본 세팅의 핵심은 어느쪽이 효율적인가? 이다.

  • @OneToOne : FetchType.EAGER

  • @ManyToOne : FetchType.EAGER

  • @OneToMany :FetchType.LAZY

  • @ManyToMany : FetchType.LAZY

    fetch 전략의 설정(실전)

    효율성- 데이터가 어느쪽으로 더 자주 사용될 것같은가를 예측해야한다.

  • default 내버려두기: 필요한 시점에 최선의 방식으로 데이터를 가져온다.

  • LAZY 사용: 연관관계가 있는 엔티티에서 자식 엔티티만 가져오는 시나리오일 때

    • 프로그래머가 로직 흐름에서 join을 의식하고 있지 않을때 LAZY사용이 적절하다.
    • LAZY 세팅이 후속 쿼리 발생 방지를 꼭 보장하고 있지 않음
    • ex: 불러들인 자식 엔티티가 서비스 레이어 어딘가에서 결국 부모 엔티티 필드를 건드렸을 경우
  • EAGER 사용: 연관관계가 있는 엔티티에서 무조건 다 가져오는 시나리오일때

    • 프로그래머가 join을 사용해야하는 상황임을 인지하고 있음
    • EAGER 세팅이 join동작을 보장하지는 않음
    • ex: Spring Data JPA 쿼리 메소드 findAll()
    • JPQL을 직접작성해서 join을 영속성 컨텍스트에 알려줘야함(대표적인 방식: queryDsl)

    N+1 query 문제

    쿼리를 한번만 날렸을 뿐인데 1+N개의 쿼리가 더생기는 오류 or 현상

N+1 query problem 해결방법

3가지방법

  • 똑똑한 lazy
    • 비지니스 로직을 면밀히 분석하여, 불필요한 연관관계 테이블 정보를 불러오는 부분을 제거한다.
    • 가장 똑똑하고 효율적인방법
  • eager fetch + join jpql
    • eager fetch로 설정해서 join쿼리를 이용해서 데이터를 받아오거나 보낸다.
    • join 쿼리를 직접 작성하는 방법은 다양(@Query,querydsl,..)
    • 쿼리 한 번에 오긴 하겠지만, join쿼리 연산 비용과 네트워크로 전달되는 데이터가 클 수 있음
  • 후속 쿼리를 in으로 묶어주기 : N+1-> 1+1로 I/O를 줄일 수 있음
    • (Join사용이 어렵거나 복잡할떄 사용하는 방법)
    • 하이버네이트 프로퍼티: default_batch_fetch_size
    • 스프링부트에서 쓰는법: spring.jpa.properties.hibernate.default_batch_fetch_size
    • 100~1000사이를 추천
    • 모든 쿼리에 적용되고, 복잡한 도메인에서 join쿼리를 구성하는 것이 골치아플 때 효율적

순환 참조 문제


각 모듈이 서로를 의존하고 있는 상태
스프링을 사용하다보면,JPA뿐만아니라 다양한 위치에서 간혹 경험하게 됨

  • 스프링 컴포넌트끼리 참조하는 경우
  • JPA에서 가장 흔하게 발생하는 순환 참조: toString()(lombok)을 사용했다가 발생하는 순환 참조 문제

해결방법은 한쪽의 참조를 제거하는것으로 간단히 해결이 된다.

profile
주니어 개발자 되고싶어요

0개의 댓글