N+1 문제에 대한 고찰(1)

Hoo-Sung.Lee·2024년 6월 29일
0

Spring

목록 보기
10/15

상황

Order와 Member는 @ManyToOne,
Order와 OrderItem은 @OneToMany 관계이다.

OrderDto는 아래와 같다.

    @Getter
    static class OrderDto {

        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address;
        private List<OrderItemDto> orderItems;

        public OrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
            orderItems = order.getOrderItems().stream()
                    .map(OrderItemDto::new)
                    .collect(toList());
            ;
        }
    }

    @Getter
    static class OrderItemDto {

        private String itemName;//상품 명
        private int orderPrice;//주문 가격
        private int count;//주문 수량

        public OrderItemDto(OrderItem orderItem) {
            itemName = orderItem.getItem().getName();
            orderPrice = orderItem.getOrderPrice();
            count = orderItem.getCount();
        }
    }

아래와 같이 코드를 작성하면 쿼리가 어떻게 나가고, 어떠한 문제가 발생할까?

복잡성을 줄이기 위해 Order와 OrderItem의 1:N관계에 대한 쿼리만 분석해보도록 하겠다.

Order Entity의 경우 자신과 연관되어 있는 엔티티들은 fetchType=Lazy로 설정되어 있으므로, Order 엔티티를 가져와 영속성 컨텍스트에 보관하고 추후에 연관된 엔티티가 필요한 시점에 가져온다.

for문을 돌면서, 각 Order.getOrderItems()를 호출할때 비로소 지연로딩으로 OrderItem을 조회하기 위한 쿼리가 나간다. 더하여 각 OrderItem.getItem을 할 때마다 Item을 조회하기 위한 쿼리가 나간다.

Order이 2개, 각 Order마다 OrderItem이 2개, 각 OrderItem은 1개의 Item을 가진다고 하면,
Order를 조회하기 위한 쿼리 1개 --> 결과 2개의 Order
각 Order마다 연관된 OrderItem을 조회하기 위한 쿼리 2개.
각 OrderItem마다 연관된 Item을 조회하기 위한 쿼리 4개.

우리는 Order를 조회하기 위한 1번의 쿼리를 위한 (1+2+4)=7번의 쿼리가 필요한 상황이다.

여러 번의 쿼리가 실행되는 것의 단점

  1. 여러번의 쿼리가 실행되면 데이터베이스와 애플리케이션 간의 네트워크 왕복 시간이 증가한다. 이는 응답시간 증가로 이어질 수 있다. 이는 지연 시간을 늘리고 전체 성능을 저하시킨다.
  2. 데이터베이스의 부하를 증가시킨다.특히 많은 양의 데이터를 로드해야 하는 경우, 데이터베이스의 자원 사용량이 증가하고 다른 요청의 처리 속도가 느려질 수 있다.

이제 1번의 쿼리를 위해 N번의 쿼리가 추가적으로 나가는 N+1문제를 해결해보도록 하자.

순서
1. Order와 Member, Order와 Delivery의 @toOne 관계를 fetch join으로 성능 최적화.
2. Order와 OrderItem의 @toMany관계의 최적화 2가지 방법 비교
(fetch join, @BatchSize)

profile
Software Engineer

0개의 댓글

관련 채용 정보