JuBin's personal study blog

[JPA] JPA N+1 문제와 해결방법 본문

JPA

[JPA] JPA N+1 문제와 해결방법

JuBin 2021. 8. 30. 09:57
반응형

JPA N+1 쿼리 문제란?

JpaRepository에 정의한 인터페이스 메소드를 실행하면 JPA는 메소드 이름을 분석해 JPQL을 생성하여 실행하게 됩니다. 이 과정에서 N+1 쿼리 문제가 발생하게 됩니다.

 

ex)

먼저 N+1 문제에 대해 먼저 간단히 설명하자면 1:N 관계를 갖는 Entity 클래스를 작성하고(Album Entity, Song Entity)

JPQL로 객체를 조회할때

 

1) EAGER 전략으로 데이터를 조회 할때

2) LAZY 전략으로 데이터를 조회 후 하위 엔티티(Song Entity)를 조회 할때

 

 N+1 이슈가 발생하게 됩니다.

 예시를 통하여 확인해 봅시다.

 

 

※ JPQL이란?

JPQL은 SQL을 추상화한 객체지향 쿼리 언어로 특정 DB에 종속되지 않고 Entity와 필드 이름을 가지고 쿼리를 만듭니다.

 


Album Entity

@Entity
public class Album {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String albumTitle;

    @Column(nullable = false)
    private String locales;

   // @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, fetch = FetchType.EAGER) // 즉시로딩
   // @OneToMany(mappedBy = "album", cascade = CascadeType.ALL, fetch = FetchType.LAZY) // 지연로딩
    private List<Song> songs = new ArrayList<>();
}

 

Song Entity

@Entity
public class Song {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private int track;

    @Column(nullable = false)
    private int length;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "album_id")
    private Album album;
}

 

Test Case 1 (Album Entity의 songs 필드 패치전략이 EAGER(즉시로딩 일때)

@Test
public void EAGER_N1_TEST throws Exception {
    List<Album> albums = albumRepository.findAll(); // N+1문제 발생
}

 

JPA N+1 발생

하나의 Album 조회시 10개의 Song row 조회하는 문제 발생

EAGER, N+1 발생

 

 

Test Case 2 (LAZY, Album Entity 조회 후 하위 Song Entity 조회 시 발생

@Test
public void LAZY_N1_TEST throws Exception {
    List<Album> albums = albumRepository.findAll(); // 지연로딩으로 인해 N+1문제가 발생하지 않음
    
    List<Song> songList = null;
    for (Album album : albums) {
    	songList = album.getSongList();  
        log.info("song size = {}", songList.size()); // N+1문제 발생
    {
}

 

JPA N+1 발생

하나의 Album 조회시 10개의 Song row 조회하는 문제 발생

LAZY, Song Entity 조회시 N+1 발생

 


N+1 해결 방안

  1. JPQL 패치 조인 사용

    - JPQL fetch join 키워드를 사용해 Album 조회 시 songList도 같이 join해서 조회합니다.

    - LAZY, EAGER 두개의 전략에 해당되는 해결방법 입니다.

@Repository
public interface AlbumRepository extends JpaRepository<Album, Long> {
	@Query("select p from Album p left join fetch p.songList")
 	   List<Album> findAllWithFetchJoin();
}

하지만 패치조인은 JPA가 제공하는 Pageable 기능을 사용할 수 없고(페이징 처리 API)

1:N 관계가 2개인 Entity에서는 사용 할 수 없습니다.

또한 데이터가 많아지면 OOM(Out Of Memory)가 발생할 수도 있습니다.

 

 

 

반응형

'JPA' 카테고리의 다른 글

[Spring-Data-JPA] 메소드 작명 키워드  (0) 2021.02.15