티스토리 뷰

JVM/Spring

[JPA] JPA N+1 문제

글을 쓰는 개발자 2021. 12. 3. 20:13
반응형

JPA N+1

연관 관계에서 발생하는 이슈로 연관 관계가 설정된 엔티티를 조회할 경우 첫 번째 조회 + 해당 갯수(N) 만큼 조회되는 현상을 말한다.

테이블은 다음과 같이 구성되어 있습니다.

 

    @Test
    void test_query_check() {
        Account account = AccountTemplate.makeTestAccountForIntegration();
        List<Organization> organizations = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            organizations.add(OrganizationTemplate.makeTestOrganizationForIntegration("테스트"+i));
        }
        Account newAccount = accountRepository.save(account);
        List<Organization> organizationList = organizationRepository.saveAll(organizations);
        List<Mission> missions = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            missions.add(MissionTemplate.makeMissionForIntegration(newAccount,organizationList.get(i)));
        }
        List<Mission> missionList = missionRepository.saveAll(missions);
        entityManager.clear();
        System.out.println("-----------------------------------------------------------------------------");
        List<Mission> myMissions = missionRepository.findAllByAccount(newAccount);
    }

다음과 같은 테스트를 진행했을 때 아래와 같은 쿼리들이 찍히게 됩니다.

너무 많아서 짤렸는데 총 11개의 SELECT문이 찍히게 됩니다.

10개 정도는 적다고 생각할 수도 있는데 이게 만약에 네이버나 배민같이 큰 트래픽이 오가는 회사에서 이러한 조회 쿼리를 날리게 된다면? 어우 생각만해도 끔찍합니다.

 

해결방법은 https://jojoldu.tistory.com/165 의 글을 참고하여 진행하게 되었다는 것을 미리 말씀드립니다.

 

여기서 소개되는 방법은 

1) Join Fetch

2) Entity Graph

 

두가지 방법이 소개되고 있는데 저는 두가지 방법 중에 2번의 방법을 택하기로 하였습니다.

그 이유는 연관관계 설정해놓은 FetchType을 사용할 수 없기 때문이다.

 

그럼 코드 수정사항이 뭔지 살펴보자.

 

List<Mission> findAllByAccount(Account account);

기존에는 다음과 같이 이렇게 되어 있었다.

 

    @EntityGraph(attributePaths = {"organization"})
    @Query("SELECT m FROM Mission m WHERE m.account = ?1")
    List<Mission> findAllByAccount(Account account);

이렇게 수정하였다.

 

outer join하는 모습을 볼 수 있다.

 

위 참고글에서 join fetch는 inner join을 한다고 되어 있는데 inner join과 outer join의 차이점이 뭔지 갑자기 궁금해졌다. 

inner join은 쉽게 생각하면 교집합을 생각하시면 되고 정확히 left outer join은 왼쪽에 있는 모든 행과 교집합의 행이 있는 것이다.

 

공통적으로 카테시안 곱(Cartesian Product)이 발생하여 orgainzation의 수만큼 mission이 중복 발생하게 된다고 참조 블로그에 적혀 있습니다. 

카테시안 곱이란 FROM 절에 2개 이상의 TABLE이 있을 때 두 TABLE 사이에 유효 JOIN 조건을 적지 않았을 때 해당 테이블에 대한 모든 데이터를 전부 결합하여 TABLE에 존재하는 행 갯수를 곱한 만큼의 결과괎이 반환되는 것

중복 등록을 없애기 위해서 Set 자료형을 쓰는 것을 추천해주고 있다. 

두 번째 방법은 DISTINCT 을 사용하여 List을 유지하는 것도 있다.

저의 경우 DISTINCT을 사용하기로 했다. 기존에 이미 List로 구현되어 있기도 했기 때문이다. 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함