article thumbnail image
Published 2023. 5. 31. 17:03

 

🔎문제 상황

yata프로젝트 중, 1:N 관계가 맺어져 있는 리뷰 전체조회, 게시글 전체조회 시 일대다 관계로 매핑된 테이블의 조회 쿼리까지 함께 날아가는 N+1문제가 발생하였다.

왼 ) 프로젝트 전체 ERD / 오) 문제가 되는 1:N 관계의 ERD 중 하나인 Yata와 YataMember

 

 

🔎원인

N+1 자체가 발생하는 이유는 한쪽 테이블만 조회하고 연결된 다른 테이블은 따로 조회하기 때문이다.
연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다.

 

왜 이런 문제가 생기냐면,,,

jpaRepository에 정의한 인터페이스 메서드를 실행하면

JPA는 메서드 이름을 분석해서 JPQL을 생성하여 실행하게 된다.

 

JPQL은 SQL을 추상화한 객체지향 쿼리 언어로서

특정 SQL에 종속되지 않고 엔티티 객체와 필드 이름을 가지고 쿼리를 한다.

 

그렇기 때문에 findAll()이란 메소드를 수행하였을 때 해당 엔티티를 조회하는 select * from Yata 쿼리만 실행하게 되는것이다.

 

JPQL 입장에서는 연관관계 데이터를 무시하고 해당 엔티티 기준으로 쿼리를 조회하기 때문이다.

 

그렇기 때문에 연관된 엔티티 데이터가 필요한 경우, FetchType으로 지정한 시점에 조회를 별도로 호출하게 된다.

 

🔎🔎fetchType

Eager : 즉시로딩 , 연관된 엔티티를 즉시 조회한다.

Lazy : 지연로딩, 연관된 엔티티를 프록시로 조회한다. 프록시를 실제 사용할 때 초기화하면서 데이터베이스를 조회한다.

ex) A와 B가 지연로딩으로 연결되어 있을 때 A를 조회하면  A를 조회하는 쿼리만 나가고, B를 조회하는 궈리는 생성되지 않는다. 실제로 B를 사용하는 시점에 B를 조회하는 쿼리가 나간다.

💡 fetch의 dafault 값 :  @xxToOne에서는 EAGER, @xxToMany에서는 LAZY

 

🔎해결 방법

미리 두 테이블을 JOIN 하여 한 번에 모든 데이터를 가져올 수 있다면 애초에 N+1 문제가 발생하지 않을 것이다. 

HOW? 두 테이블을 JOIN 하는 쿼리를 직접 작성

=> '다' 쪽에 fetchjoin을 걸어줌(QueryDsl사용)

 

https://develoyummer.tistory.com/83

 

QueryDsl이란? 사용법+ 프젝 코드에 적용하기

QueryDsl이란? 정적 타입(컴파일 시 타입에 대한 정보를 결정)을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해 주는 오픈소스 프레임워크 왜쓰는가? JPA를 사용하다 보면 기본 기능으로 해결되지

develoyummer.tistory.com

 

우리 프젝 코드

결과를 보면 쿼리가 1번만 발생하고 미리 yata와 yatamember 데이터를 조인(Inner Join)해서 가져오는 것을 볼 수 있다.
예를 들어 리뷰 조회 시 일대 다로 연결된 리뷰 체크리스트 를 패치조인 해주어 한번에 가져오게 하는 것

 

💡주의점!

카테시안 곱(Cartesian Product)이 발생하여 1의 수만큼 N의 중복 데이터가 존재할 수 있다. 그러므로 중복된 데이터가 컬렉션에 존재하지 않도록 주의해야 한다.

 

😃해결법

1.컬렉션 Set을 사용(중복허용X)

2.distinct를 사용

 

 

https://develoyummer.tistory.com/92

 

[프로젝트] 카테시안 곱 문제의 발생과 해결

N+1 문제를 해결하기 위해서 fetchJoin을 사용하여 해결해 주는 과정에서 yata와 연결된 yatamember 중 수락된 yatamember를 계산해서 보여줄 필요가 있었는데 수가 비정상적으로 많게 나오는 문제가 발생

develoyummer.tistory.com

 

 

 

⏱️시간비교

 

 

 

 

 

복사했습니다!