JPA 와 N + 1
🙈

JPA 와 N + 1

Created
Mar 18, 2024 05:55 AM
Last edited time
Last updated March 18, 2024
Tags
Backend
Spring
Language
Java
URL

Intro::

자바 표준 ORM 인 JPA 에서 발생할 수 있는 성능 저하 문제인 N + 1 에 대해 알아보자.

JPA 란?

JPA(Java Persistence API)는 Java에서 제공하는 ORM 기술의 표준 인터페이스로, 객체 지향 프로그래밍과 관계형 데이터베이스를 매핑해주는 역할을 합니다. ORM은 객체와 데이터베이스 테이블을 매핑하여 객체 간의 관계를 바탕으로 SQL을 자동으로 생성, 실행해 줍니다. 이 과정에서 개발자는 객체 지향적인 코드로 데이터를 다룰 수 있게 되며, SQL을 직접 작성하는 수고를 덜 수 있습니다.

n+1 문제란?

n+1 문제는 연관 관계에 있는 엔티티를 조회할 때 발생합니다. 예를 들어, 게시글(Post)과 댓글(Comment)이 1:N 관계에 있을 때, 게시글을 조회하며 연관된 댓글들도 함께 조회하고자 할 때 발생합니다.
  1. 먼저, 게시글을 조회하는 SQL이 실행됩니다. (1번 실행)
  1. 각 게시글에 대해 연관된 댓글들을 조회하기 위해, 게시글 수만큼 추가 SQL이 실행됩니다. (n번 실행)
💡
결과적으로, n개의 게시글을 조회할 때, 1번의 게시글 조회와 n번의 댓글 조회 SQL이 실행되어 총 n+1번의 SQL이 실행되는 것입니다. 이는 데이터베이스와의 네트워크 통신 비용을 증가시키고, 전체 성능 저하를 일으킵니다.

해결 방법

n+1 문제를 해결하기 위한 대표적인 방법은 다음과 같습니다.
  1. Fetch Join 사용: JPA에서 제공하는 Fetch Join을 사용하면 연관된 엔티티를 한 번의 쿼리로 함께 조회할 수 있습니다. JPQL에서 JOIN FETCH 구문을 사용하여 연관된 엔티티를 함께 로딩합니다.
  1. Entity Graph 사용: Entity Graph는 JPA 2.1부터 도입된 기능으로, 엔티티의 특정 속성을 로딩 전략을 정의하여 사용합니다. 특정 조회 작업에서만 연관된 엔티티를 함께 로딩하고 싶을 때 유용합니다.
  1. Batch Size 설정: @BatchSize 어노테이션을 사용하거나, 글로벌 설정으로 batch size를 설정할 수 있습니다. 이 방법은 연관된 엔티티를 조회할 때, 설정된 크기만큼의 묶음으로 SQL을 실행하여 네트워크 비용을 줄이고 성능을 향상시킵니다.

Fetch Join

Fetch Join은 JPQL 또는 Criteria API를 사용하는 쿼리에서, 연관된 엔티티나 컬렉션을 한 번의 쿼리로 함께 조회하는 기능입니다. 이를 통해 연관된 데이터를 즉시 로딩할 수 있으며, n+1 문제를 효과적으로 해결할 수 있습니다.

Lazy Loading (fetch=LAZY)

fetch=LAZY 설정은 연관된 엔티티가 실제로 접근되는 시점까지 로딩을 지연시키는 로딩 전략입니다. 즉, 엔티티를 처음 로드할 때는 연관된 엔티티를 로드하지 않고, 해당 엔티티에 실제로 접근할 때(예: getter 메서드 호출 시) 관련 쿼리를 실행하여 데이터를 로드합니다.

Lazy Loading과 N+1 문제

N+1 문제의 발생: Lazy Loading 전략을 사용할 때, 실제로 N+1 문제가 발생할 수 있습니다. 이는 한 번의 연산으로 여러 엔티티를 로드할 때 각각의 연관된 엔티티를 로드하기 위해 추가적인 쿼리가 실행되기 때문입니다. 예를 들어, 여러 게시글을 로드한 후 각 게시글에 연관된 댓글을 조회할 때, 게시글 하나당 댓글을 조회하기 위한 별도의 쿼리가 실행되므로, 게시글 N개에 대해 N개의 추가 쿼리가 실행되는 결과를 초래합니다.
 

References::

Loading Comments...