← 질문 목록
#17깊이 0
JPA에서 N+1 쿼리는 왜 생기고 무엇으로 막는가?
연관을 지연 로딩으로 두고 목록을 순회할 때 생긴다. 목록을 가져오는 질의 하나에 더해, 원소마다 연관을 채우는 질의가 한 번씩 더 나간다.
앱→DB
주문 목록 조회. 질의 1번
앱→DB
1번 주문의 회원 조회
앱→DB
2번 주문의 회원 조회. 원소 수만큼 반복된다
즉시 로딩으로 바꾸는 것은 답이 아니다. 그러면 연관이 필요 없는 조회에서도 매번 조인이 붙는다. N+1이 사라지는 대신 모든 질의가 무거워진다. 문제를 옮긴 것이지 푼 것이 아니다.
해결은 필요한 자리에서만 한 번에 가져오는 것이다. fetch join이나 @EntityGraph로 그 조회에 한해 함께 읽는다. 어디를 함께 읽을지는 화면이 결정하므로, 매핑이 아니라 질의 쪽에 두는 것이 맞다.
주의
fetch join에도 함정이 있다. 컬렉션을 조인하면 행이 곱해져 부모가 중복되고, 그 상태로는 페이징을 데이터베이스에 맡길 수 없다. 하이버네이트는 이때 전부 읽어 메모리에서 자르며 경고를 남긴다. 목록이 크면 그대로 사고다.
그래서 페이징이 함께 필요한 화면에서는 batch size를 쓴다. 연관을 원소마다 한 번씩이 아니라 IN 절로 묶어 몇 번에 나눠 읽는다. 질의 수가 N에서 N/배치로 줄고 페이징은 데이터베이스가 그대로 맡는다.