노트

N+1 문제

N+1 Query Problem

백엔드#jpa · 연결된 개념 10개

쉽게 말하면

N+1 문제는 장보기 목록을 한 번 받아 놓고, 적힌 물건마다 마트를 따로 한 번씩 다녀오는 거예요. 물건이 적을 땐 티가 안 나다가, 많아지면 왕복 횟수가 그만큼 늘어 느려지죠.

비유가 깨지는 곳 원인은 대개 지연 로딩이라 코드에 반복 조회가 안 보여도 생겨요. fetch join이나 @EntityGraph로 한 번에 가져오거나 배치 페치로 묶고, SQL 로그로 쿼리 수를 직접 세어 봐요.

목록을 한 번 조회(1)한 뒤 각 행의 연관 엔티티를 따로 조회해서 쿼리가 N번 더 나가는 문제. 데이터가 적은 개발 환경에서는 잘 보이지 않는다. 원인은 대개 지연 로딩이다.

해법은 상황에 따라 고른다.

  • fetch join: JPQL(Jakarta Persistence Query Language) join fetch로 한 번에 가져온다(JPQL과 @Query). 컬렉션을 fetch join하면 페이징과 함께 쓰기 어렵다
  • @EntityGraph: 메서드마다 함께 가져올 연관을 선언한다
  • 배치 페치(batch fetching): default_batch_fetch_size로 지연 로딩을 IN (?, ?, …) 묶음 조회로 바꾼다
  • DTO(Data Transfer Object) 프로젝션(projection): 필요한 컬럼만 조회한다
sequenceDiagram
  participant A as 애플리케이션
  participant DB as DB
  Note over A,DB: 지연 로딩 그대로 - 1 + N번
  A->>DB: 목록 조회 1번
  loop 행마다 N번
    A->>DB: 연관 엔티티 조회
  end
  Note over A,DB: fetch join - 1번
  A->>DB: join fetch로 목록과 연관을 함께 조회

무엇보다 SQL 로그를 켜 두고 쿼리 수를 눈으로 확인하는 습관이 먼저다.

책: 8장. 운영에서 터지는 것들 — N+1

출처: Hibernate ORM User Guide: Batch fetching · Entity graph · Best practices: Fetching

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • JDBC와 JdbcTemplate

    JDBC(Java Database Connectivity)는 자바가 여러 DB와 통신하기 위한 표준 API이고, Hibernate·MyBatis·스프링 JDBC 모두 그 위에 있다. JdbcTemplate은 스프링이 JDBC의 반복 작업(연결 열고 닫기, 예외 변환, 결과 순회)을 대신해 주는 클래스다.

  • 테스트 냄새

    테스트 코드나 테스트 습관에서 나는, 더 깊은 문제를 알리는 신호. 제라드 메스자로스의 xUnit 테스트 패턴 정리가 이름을 붙였다.

  • 최종 일관성

    최종 일관성(eventual consistency)은 "지금 당장은 저장소마다 값이 다를 수 있지만, 새 변경이 멈추면 결국 같아진다"는 보장이다. 원본 DB와 검색 색인·캐시·다른 서비스처럼 물리적으로 분리된 저장소를 한 트랜잭션으로 묶을 수 없을 때 받아들이는 일관성 모델(consistency model)이다.

  • 동기·비동기와 블로킹·논블로킹

    동기·비동기는 결과를 언제 어떻게 받느냐, 블로킹·논블로킹은 기다리는 동안 호출한 스레드가 멈추느냐의 문제다.

  • 읽기 전용 복제본

    읽기 전용 복제본은 주 DB(primary, writer)의 데이터를 복제해 읽기 요청만 받는 DB 인스턴스다. 쓰기는 writer 한 곳에서 처리하고, 읽기를 여러 복제본으로 나눠 부하를 분산한다.

보기 옵션