배경
문제
- @OneToMany 로 매핑된 테이블에 대해 N+1 쿼리 발생
- @OneToOne 으로 User와 매핑된 PiBasic 에서 N+1 쿼리 발생
- 연관관계 주인이 아닌 (FK가 없는) 엔티티에서 OneToOne 관계의 엔티티 조회시, N+1 쿼리 발생
User → PiBasic
당연히 User는 PiBasic의 PK를 모르기때문에 한 번에 조회 불가 → N+1 쿼리 발생
- @ManyToOne 으로 매핑된 연관 엔티티의 fetch 타입이 EAGER라 해당 엔티티 데이터가 필요없음에도 불구하고 매번 조회함
- Goal 에서 GoalDto로 변환하는 과정에서 Goal에 있는 모든 연관관계를 조회하여 GoalDto에 매핑해야되기때문에 GoalDto로 변환하는 [Goal의 개수 * 연관관계 개수] 만큼 N+1 쿼리 발생
해결방법
- 1번 이슈
- batchsize를 지정하여 N+1 쿼리를 IN 절이 포함된 최소한의 쿼리로 변환
- 2번 이슈
- @EntityGraph 를 사용하여 필요한 모든 데이터를 N+1 문제 없이 조회
- 3번 이슈
fetch = FetchType.LAZY
지정
- 4번 이슈
- 요건 해결하기 어려운게 Dto로 변환시 Goal과 연관있는 모든 데이터를 조회해야하는데 OneToMany로 매핑된 연관 Entity를 한방 쿼리로 조회하는게 사실상 불가능 → N+1 쿼리가 불가피
24/06/28 update
- Table에 teamId 추가해서 INDEX 생성함
- N+1 문제가 발생하는 부분 전부 직접 IN절로 조회하여 데이터 세팅해줌
- Entity가 아닌 Dto로 조회하도록 수정
- Entity로 조회하면 연관관계가 많이 설정되어있는 Entity의 경우, 예상치 못한 쿼리가 다수 발생함
- 실제 필요한 데이터만 조회하여 데이터를 Setting 해주면 불필요한 쿼리 호출을 줄일 수 있음
- 하지만, Dto를 조회하여 비즈니스 로직을 Dto로 받도록 하면 해당 기능 전반에 걸친 비즈니스 로직자체가 Dto에 의존적인 상황이 됨
- 이 때 문제가, 특정 기능의 응답값이 변경되는등 Dto에 대한 명세가 바뀔 경우 해당 Dto를 의존하고 있는 비즈니스 로직이 영향을 받을 수 있음
- 또한 Entity를 의존할때보다 구현이 복잡해질 수 있음
- 각 Dto 명세에 맞도록 로직을 작성해야하기 때문
- 결국, 구현을 빠르게 해야하는 상황이라면 Entity로 조회
- 하지만 성능을 고려해야하는 상황이라면 필요한 데이터만 조회 (Dto)하는게 맞다고 생각함