N+1 문제를 알아보았다.
먼저 N+1 문제가 발생하는 시점을 알아보자.
다음과 같이 한 엔티티에 ManyToOne으로 연관관계가 설정되어 있는 상황이다. FetchType.Lazy로 지연로딩이 설정되어 있다. 지연로딩이 되어 있으면 .getMember()를 하는 시점에 쿼리가 나간다.
데이터는 간단하게 삽입했다.
간단한 코드로 확인해보자. 다음 코드는 모든 Article을 조회하고, article.getMember()로 Article에 연관된 Member 객체를 조회하는 코드이다.
아래의 결과처럼 쿼리가 두 번 나갔음을 알 수 있다.
아직 데이터가 한개밖에 없어서 감이 안오기 때문에 Article 개수를 3개로 늘렸다. 그리고 이번엔 지연로딩이 아닌 즉시로딩으로 조회했다.
결과는 아래와 같이 4개의 쿼리가 나간다. Article을 조회하는 쿼리1개, 3개의 article에서 Member객체를 조회하는 쿼리 각각 한 개. 총 3개이다. 이 기능으로 조회되는 Article의 개수만큼 추가 쿼리가 나가게 된다.
이러한 문제가 발생하는 이유는 조인이 되지 않은 쿼리가 나가기 때문이고, 조인을 하면 자연스레 문제가 해결된다.
이를 해결하려면 페치조인을 하면 된다.
그냥 사용하고자 하는 메소드 위에 @Query 어노테이션을 달아주고 jpql 쿼리를 작성하면 된다.
결과를 이전과 비교해보자.
이전 결과
Hibernate: select article0_.id as id1_0_, article0_.body as body2_0_, article0_.member_id as member_i4_0_, article0_.title as title3_0_ from article article0_
Hibernate: select member0_.id as id1_2_0_, member0_.email as email2_2_0_, member0_.nickname as nickname3_2_0_, member0_.thumbnail as thumbnai4_2_0_ from member member0_ where member0_.id=?
Hibernate: select member0_.id as id1_2_0_, member0_.email as email2_2_0_, member0_.nickname as nickname3_2_0_, member0_.thumbnail as thumbnai4_2_0_ from member member0_ where member0_.id=?
Hibernate: select member0_.id as id1_2_0_, member0_.email as email2_2_0_, member0_.nickname as nickname3_2_0_, member0_.thumbnail as thumbnai4_2_0_ from member member0_ where member0_.id=?
수정 후 결과
Hibernate: select article0_.id as id1_0_0_, member1_.id as id1_2_1_, article0_.body as body2_0_0_, article0_.member_id as member_i4_0_0_, article0_.title as title3_0_0_, member1_.email as email2_2_1_, member1_.nickname as nickname3_2_1_, member1_.thumbnail as thumbnai4_2_1_ from article article0_
inner join member member1_ on article0_.member_id=member1_.id
같은 기능, 같은 결과이지만 쿼리가 1개로 줄었음을 확인할 수 있다.
그런데 만약 .getMember()를 호출하는 경우도 있고, 그렇지 않을 경우도 있을것이라 생각된다. 그런 경우엔 오히려 조인이 쿼리 비용을 쓸모 없이 증가시키는 경우가 아닐까 생각된다.
조인을 한다고 비용이 0인것이 아니기 때문에 비용에 대한 의문이 있었는데, 상황에 따라 다른것으로 보이니 잘 고려해야할 것 같다.
그렇지만 N+1의 경우에 자칫하면 한 번의 조인을 포함한 하나의 쿼리로 해결 가능한 것을 N번 (수만 번이 될지도 모르는)의 쿼리가 추가되는 셈이니 조인해서 해결하는게 좋을 것 같다.
https://stackoverflow.com/questions/1067016/join-queries-vs-multiple-queries
++ 페치조인으로 쓰고 실 쿼리는 이너조인이 나가길래 이너 조인을 쓰면 같은 해결 방법인줄 알았는데 그게 아니라는 것을 얼마전에 알았다.
https://velog.io/@chs98412/%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%ED%95%B4%EC%86%8C