[리팩토링] 쿼리 성능 개선 (Fetch Join , PageNation)

이정환·2023년 8월 14일
0

[개인 프로젝트]

목록 보기
6/8

기존 코드 findAll과 40번 쿼리요청

현재 코드는 프론트엔드에서 모임 목록 페이지에 접근하면 1번 페이지 열리면서 가장 최근의 Room 데이터 10개 호출해서 렌더링한다.

Room 테이블은 HostUser, Category, RoomImage 테이블과 관계 맺고 있는 상태다. Dto 변환시 각 테이블 데이터가 필요하기에 10개 데이터 호출되면 각 테이블데이터까지 포함해서 약 40번 쿼리 발생했다. 문제다.

Fetch Join과 PageNation

1개의 데이터 호출시 Fetch Join 사용하면 연관된 테이블 쿼리를 1번으로 압축 시킬 수 있었다. findAll도 가능할 것이라 예상했다. 아래 처럼 서비스와 레포지토리 코드를 수정 후 실행했다. 하지만 에러 발생했다.

Fetch Join는 4개의 연관 엔티티 데이터를 가져오고, Pagenation은 10개의 로우 데이터를 Limit 등 제한 조건적용해서 데이터를 가져온다. 하지만 위 두 기능에서 데이터 로딩때 데이터 중복 및 예상되지 않은 데이터 꼬임 및 성능저하 발생할 수 있다고 한다.

그런 이유로 Jpa에서는 두 기능 함께 사용을 미지원 및 상황에 따라서 제한한다고 한다. 그럼 어떻게 하면 좋을까?

//service

    @Override
    public List<RoomDto> getRoomList(Integer page, Integer size) {
        PageRequest pageRequest = PageRequest.of(page-1, size, Sort.by("id").descending());
        Page<Room> roomsPage = roomRepository.findAllWithCategoryAndHostUsersAndImage(pageRequest);
        
        List<Room> rooms = roomsPage.getContent();
        if (rooms.isEmpty()) {
            return new ArrayList<>(); 
        }

        List<RoomDto> roomDtos = rooms.stream().map(r -> roomMapper.toRoomDto(r)).collect(Collectors.toList());

        return roomDtos;
    }
//repository

    @Query("SELECT DISTINCT r FROM Room r " +
            "LEFT JOIN FETCH r.category " +
            "LEFT JOIN FETCH r.hostUserList " +
            "LEFT JOIN FETCH r.roomImage"
    )
    Page<Room> findAllWithCategoryAndHostUsersAndImage(Pageable pageable);

Batch (40 -> 4)

먼저 떠오른 생각은 모든 데이터 다 호출 후 Page구분 하려고 했다. 문제는 데이터가 쌓일수록 문제도 쌓일 것이라 판단했다.

결국 사용한 방법은 Fetch Join을 빼고 application-dev.yml에 Batch 값을 10으로 셋팅했다. 최대 여러 번 쿼리를 한 번에 묶어서 요청하는 방식인데 한 페이지에 10개씩 데이터 필요해서 10으로 할당했다.

이 후 Room 목록 10개 조회하니 40번의 쿼리는 4번으로 줄었다.(page로 인해 생긴 Count 쿼리 추가하면 5번)
임시적으로 이전보다는 쿼리 최적화 시켰다.

할 수 있으면 1번의 쿼리로도 가능할까 싶은데 아직은 잘 모르겠다. 나중에 알게되면 그 방법 사용할 계획이다.

1개의 댓글

comment-user-thumbnail
2023년 8월 14일

큰 도움이 되었습니다, 감사합니다.

답글 달기