본문 바로가기
BE 프로젝트 일대기

GNIMS - JPA 쿼리 최적화를 해봅시다!

by 포도자몽 2023. 2. 14.

안녕하세요. 그님스에서 일정, 팔로우 도메인을 맡은 BE 이재헌입니다. 저는 오늘 JPA를 사용하면서 성능을 위해 꼭 해결해야할 문제 중 하나인 N + 1에 대해 공유하려고 합니다.

 

목차

1. 그님스 프로젝트로 N + 1문제에 대해 알아봐요.

2. 그래서 N + 1은 왜 발생하나요?

3. 어떻게 해결하실거죠?

4. 성능 비교


그님스 프로젝트로 N +1 문제에 대해 알아봐요:)

1번 쿼리를 날렸을 때 의도치 않게 연관된 엔티티의 쿼리가 N번 추가로 실행되는 것을 말합니다. 현재 제가 맡은 도메인의 ERD는 아래와 같습니다.

스케줄 상세 조회 API의 응답하는 데이터 구조는 아래와 같습니다.

보시는 봐와 같이 단건의 스케줄 조회 API에서는 이벤트, 유저 엔티티가 스케줄 엔티티와 연관되어 있습니다. 그래서 1개의 스케줄을 조회하기 위해서는 1개의 이벤트3개의 User 엔티티를 조회해야 합니다. 위 응답에서는 한 스케줄에 User 엔티티가 3개 있기 때문에 실제 쿼리는 5번 실행됩니다. 아래 사진을 통해 실제 쿼리 실행 결과를 확인해봅시다.

이처럼 N + 1은 엔티티 1개를 조회하기 위해서 연관된 엔티티 N번을 추가로 조회하는 것입니다.

위에서 1개의 Schedule과 연관된 엔티티는 Event 1개, User 3개이기 때문에 총 1 + 4 가 발생합니다.


그래서 N + 1은 왜 발생하나요?

N + 1은 지연 로딩, 즉시 로딩 모두 발생합니다. 객체 그래프를 탐색하면서 객체를 초기화해야 할 때 발생하게 됩니다.

 


어떻게 해결하실거죠?

N + 1 문제를 해결할 수 있는 방법은 3가지에요.

1. join fetch

join fetch는 SQL에는 없는 문법입니다. 원하는 데이터를 한 번에 조회할 수 있습니다.

join fetch 예시

2. projection

저는 주로 해당 방법을 많이 활용 했습니다. 딱 필요한 데이터만 불러올 때 용이하고 엔티티에 연관관계가 단방향일 때도 유용하게 사용할 수 있습니다. 다만 이 방법에 경우에는 DTO를 반환하기 때문에 재사용성이 떨어진다는 단점이 있어요!

 

Root

 


성능 비교

환경

로컬 서버

H2 데이터베이스

 

추후 ec2 - mysql 을 이용한 성능 테스트를 실시할 예정입니다.

 

테스트 케이스

  • Schedule 테이블 레코드 16080개 중, 1030개 조회
  • Schedule 테이블 레코드 16080개 중, 2990개 조회
  • Schedule 테이블 레코드 45545개 중, 30개 조회
  • Schedule 테이블 레코드 45545개 중, 5248개 조회

1.Schedule 테이블 레코드 16080개 중, 1030개 조회

 

1) 미적용 - 평균 조회시간 약 1500ms

2) 적용  - 100ms

 


2.Schedule 테이블 레코드 16080개 중, 2990개 조회

 

1) 미적용 -  평균 조회시간 약 6000ms

2) 적용 - 평균 조회 시간 500ms


3. Schedule 테이블 레코드 45545개 중, 30개 조회

 

1) 미적용 -  평균 조회시간 약 80ms

 

2) 미적용 -  평균 조회시간 약 25ms


4. Schedule 테이블 레코드 45545개 중, 5248개 조회

 

1) 미적용 -  평균 조회시간 약 8000ms

 

2) 적용 -  평균 조회시간 약 2200ms