[WIL] 240318~240324

라미·2024년 3월 25일

TIL

목록 보기
25/25

이번주에 나에게 있어 가장 큰 이수였던 N+1 문제에 대하여 정리해 보려 한다.
추후 팀원이신 진선님이 해결하신 SECURITY MULTY LOGIN,
유하님의 결제 API 적용법
민기님의 CI/CD, DOKER 등등 의 내용에 대해서도 추가해야지!


JPA N+1 문제

성능개선을 위한 N+1 문제 원인 및 해결 방법 알아보기

프로젝트 마지막이 다가오고 있다.

아니.....그런데 말입니다...?

나는 그냥....메인 페이지 로드만 했는데 쿼리....너 왜 세개씩 날라가?
N+1 너구나!! N+1 개선을 위해 공부를 해보자!


FetchType(EAGER,LAZY)

이 문제를 알기전에 즉시로딩(EAGER)지연로딩(LAZY)의 개념에 대해서 먼저 알아야한다.

즉시 로딩(EAGER Loading) : 특정 엔티티를 조회할 때 연관된 모든 엔티티를 같이 로딩한다.
'즉시 로딩'은 항상 외부 조인(OUTER JOIN)을 사용한다. ( 외부 조인보다 내부 조인(INNER JOIN)이 성능 최적화에 더 유리하다.)

지연 로딩(Lazy Loading) : 엔티티 조회시 해당 정보가 필요한 시점에 조회를 한다.
(연관관계에 있는 테이블을 다 가져오지 않는다.)
지연로딩이 된 엔티티의 정보를 조회하려면
반드시 영속성 context에 들어있어야한다. (@Transactionl 이 적용되야 한다.)

🐳 각 연관관계의 default FetchType
@ManyToOne : EAGER
@OneToOne: EAGER
@ManyToMany : LAZY
@OneToMany : LAZY


N+1 넌 누구냐?

N+1 문제란 1번의 쿼리를 날렸을 때 의도하지 않은 N번의 쿼리가 추가적으로 실행되는 것을 의미한다.

연관관계가 설정된 엔티티사이에서 발생하며 조회된 엔티티의 개수 만큼 연관된 엔티티를 조회하기 위해 추가적인 쿼리가 발생하는 문제이다!

마치 나는 셀렉트 한번해서 store 데이터만 가져오면 되는데 연관된 menu,order 까지 조회 쿼리를 3~4개씩 날리는 너같은 녀석...!!

🐳 발생 시점

  • JPA Repository를 사용하여 인터페이스 메소드 호출할 때 (Read 시)

🐳 발생 하는 곳

  • 1:N OR N:1 관계를 가진 엔티티를 조회할 때 발생

🐳 발생 상황

  • JPA Fetch 전략이 EAGER 전략으로 데이터를 조회하는 경우
  • JPA Fetch 전략이 LAZY 전략으로 데이터를 가져온 이후연관 관계인 하위 엔티티를 다시 조회하는 경우

🐳 발생 원인

  • JPA Repository로 find 시 실행하는 첫 쿼리에서 하위 엔티티까지 한 번에 가져오지 않고, 하위 엔티티를 사용할 때 추가로 조회하기 때문에
  • JPQL은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL만 가지고 SQL을 생성하기 때문에

EAGER(즉시 로딩)인 경우
1. JPQL에서 만든 SQL을 통해 데이터를 조회
2. 이후 JPA에서 Fetch 전략을 가지고 해당 데이터의 연관 관계인 하위 엔티티들을 추가 조회
3. 2번 과정으로 N + 1 문제 발생

LAZY(지연 로딩)인 경우
1. JPQL에서 만든 SQL을 통해 데이터를 조회
2. JPA에서 Fetch 전략을 가지지만, 지연 로딩이기 때문에 추가 조회는 하지 않음
3. 하지만, 하위 엔티티를 가지고 작업하게 되면 추가 조회가 발생하기 때문에 결국 N + 1 문제 발생


N+1 해결 방법

N+1 그래서 어떻게 해결하는데요...?

FetchJoin(패치 조인)

한 번의 쿼리로 연관관계에 있는 객체도 가져온다

Fetch join
조회의 주체가 되는 엔티티와 관련 엔티티까지 함께 조회하여 즉시 원하는 정보를 사용할 수 있도록 데이터를 로드해 온다. 이를 객체 그래프 로드라고 한다.
한 번의 쿼리로 필요한 정보를 모두 가져와서 성능을 향상 시킬 수 있다.


FETCH JOIN 이 만능은 아닌데....우리 프젝에서는 FETCH JOIN 말고는 적용 할 부분이 없어서 일단! FETCH JOIN 을 적용 시키기로 했다!

페치조인적용기 😅

데이터를 너무 조금만 넣고 확이한 상태라서 그렇다할 성능개선에 대해 확인하지 못 한 상태이다.

다른팀에 가서 물어봤더니 테스트 코드로 30만건을 넣어보고 확인하셨다고.........흠.....죄삼다 30건 넣고 확인해보려 했던 저의 무지함에 반성합니다....


아휴....개미 겉핥기식으로 얼렁뚱당 N+1 해결완료~

하지만 FETCH JOIN을 사용하면 분명히 주의할 점도 있다.

~ToMany 관계인 엔티티에 대해서는 페이징을 진행할 수 없고, 둘 이사아의 컬렉션을 페치 조인하는 데이터가 부정합하게 조회된다!

이번에 프로젝트를 진행하면서 느낀점은 QueryDsl에 대한 공부의 필요성을 느꼈다...!

아니면 그냥 마의바티스 사용하는게 속 편할 것 같다....에휴...............................


JPQL, QueryDsl, TDD 등에 대하여 좀....공부 필요성 심각하게 느끼는 중

지금은 얼렁뚱땅 넘어갔지만..한번 날 잡고 개념 정리를 다시 해야 할 것 같다!!!


📋참고자료
https://ttl-blog.tistory.com/1135
https://hungseong.tistory.com/68
https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1
https://curiousjinan.tistory.com/entry/jpa-nplusone-problem-solutions

0개의 댓글