ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA와 하이버네이트 N+1 최적화 과정
    spring/JPA 2021. 4. 8. 16:21

    배경

    진행하는 프로젝트의 테이블이 단순해서 그런지 JPA 성능 이슈를 겪어본 적이 없다. 강의를 들어며 강조한 N+1문제도 딱히 그러려니 한데 워낙 강조되는 내용이기도 하고 왜 중요한지도 이해한 상태다. 다만, 써먹을 일이 없다보니 자꾸 까먹으니 정리해두자.

     

     

    ToOne에서 최적화

    관계가 ToOne이라면 이미 best solution이 있다. 그냥 바로 join fetch를 쿼리 안에 녹이는 방법이고 이는 사실 RDB와도 직관적으로 다르지 않다. 잘 생각해보면 어렵지 않으니 간단하게 넘어가자.

     

    컬렉션 최적화

    컬렉션에서 최적화를 하려면 케이스 별로 다르게 최적화를 해야 한다.

     

    단 하나의 컬렉션만 포함하며 페이징이 필요 없는 경우

    이 경우는 ToOne과 마찬가지로 join fetch를 적용하면 되는데 단 distinct 키워드만 붙여주면 된다. JPA의 distinct는 SQL의 distinct외에 was 내에서 부가적인 기능을 해준다. RDB관점에서 본다면 1:N 관계든 N:1 관계든 실제로 나오는 결과 레코드 개수는 최대 N개다. 반면, JPA관점에서 1:N관계를 join한다면 하나의 객체와 최대 N개의 연관객체를 필요로한다. 이때 작업을 매핑해주는 게 distinct 키워드다. 

     

    컬렉션이 둘 이상이거나 페이징을 지원해야 하는 경우

    우선 조회할 컬렉션이 둘 이상인 경우 데이터의 양은 1*N*M*...으로 매우 커진다. DB는 이러한 결과밖에 줄 수 없고 결국 이를 처리하는 건 JPA의 몫이다. 1:N까지는 어찌어찌 해결했지만 그 이상은 무리인 듯하다. 결국 join fetch를 포기하면 N+1문제는 발생할 수밖에 없다. 이때 application.yml에 옵션을 추가해주자.

     

     

    이 옵션을 이해하기 위해 간단한 테이블 구성 및 테스트를 진행해보겠다.

    Test엔티티와 TestItemA엔티티는 1:N관계를 양방향을 맺고 있다. 이때 Test엔티티를 전부 조회한 경우를 생각해보자. (참고로 여기선 Test의 컬렉션이 List<TestItemA>하나로 join fetch가 가장 적절한 해결책이다)

    우선 Test를 전부 조회한 뒤 List<Test>를 얻어온다.(LAZY든 EAGER든 똑같이 동작한다) 따로 옵션을 지정하지 않았다면 List<Test>에 조회된 각 Test인스턴스 별로 TestItemA를 조회하려 할 것이다. 만약 List<Test>의 size가 10이라면 쿼리의 개수도 1+10이 될 것이다.

     

    이때 옵션을 설정하면 실제로 나가는 쿼리의 수는 1+1개다. 내부적으로 in 절을 이용해 조회할 Test를 버퍼링한 뒤 한 번에 처리하는 것 같다.

     

    다만 여기서 주의할 점이 있다.

    엔티티를 설계할 때 EAGER로 세팅해줘야 한다는 점이다. (필자는 LAZY로 했을 때 in절 쿼리가 사용 안됨) 나름대로 이유를 추측해보면 fetch전략이 LAZY인 경우 모든 Test에 대해 TestItemA를 조회한다는 보장이 없다. 결국 batch처리 전략이 모호해지는 것이다. 반면 fetch전략이 EAGER인 경우 batch전략을 확실히 세울 수 있다. 

     

     

    마치며

    관련 글을 꼼꼼히 읽지 않아서인지 모르겠지만 마지막에 언급한 부분은 딱히 내용이 없던 것 같다.

    좀 더 공부하고 수정할 수 있으면 해야겠다.

    'spring > JPA' 카테고리의 다른 글

    연관관계 주인 mappedBy 끝내기  (0) 2021.05.05
    JPA 다른 모듈에서 ENTITY 스캔하기  (0) 2021.04.29
    JPA open session in view 설정하기  (0) 2021.04.08
    JOIN FETCH와 JOIN  (0) 2021.03.02
    양방향 연관관계와 CASECADE 옵션  (0) 2021.02.25

    댓글

Designed by Tistory.