전체 보기
🟢

API 성능 테스트, 12초→0.57초로 향상(with batch fetching)

작성일자
2023/10/24
태그
SPRING
프로젝트
FIS
책 종류
1 more property

API 성능 테스트 (with fetch join)

(1) 예상

1번 방식) 기존 api에서 center 정보 가져올 때
기존 쓰던 대로 api 쓰면 됨(서버 통신 그대로)
센터 정보 찾는 쿼리 날릴 때 최적화 쉬움
jpa에서 제공하는 fetch join+batch 기능을 사용해서 쿼리 수가 n/배치사이즈으로 줄어들고 중복데이터는 안가져와서 최적화 쉬움
select * from center where id in (1,2,3)
같은 트랜잭션 내에서 조회해오기에 데이터 일관성을 보장할 수 있음
2번 방식) 새로운 api에서 center 정보 추가로 가져올 때
api를 한 번 더 날려야 함(서버 통신 + 1)
센터 정보 찾는 쿼리 날릴 때 최적화 어려움
id 100개면 쿼리도 100번 날려야 함 → n
select * from center where id=1
select * from center where id=2
설령 in 절 사용하더라도 배치 처리 따로 또 해주지 않으면 최적화 힘듦
select * from center where id in (1,2,1) → 중복은 in이 알아서 처리해준다.
배치 처리만 안된다.

(2) 결과

아래 코드를 컨트롤러 단에 넣어주어 측정했다. 아 근데 포스트맨 사용 중이면 포스트맨에도 실행 시간 뜬다.
StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 실행 코드 stopWatch.stop(); System.out.println(stopWatch.prettyPrint()); System.out.println("코드 실행 시간 (s): " + stopWatch.getTotalTimeSeconds());
Java
복사
2번 방식 테스트 전 짧막한 수정)
어….. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 70개 가져오는데 12초
문제의 레거시 쿼리…..
@Override public Center findByIdAndFetchAll(Long id) throws NoResultException, NonUniqueResultException { // list "select center from Center center join Center.Call on 조건 join return em.createQuery("select distinct center from Center center " + "left join fetch center.callList as call " + "left join fetch call.user " + "where center.id = :id ", Center.class) .setParameter("id", id) .getSingleResult(); }
Java
복사
findById 사용하게 바꿔줬다. fetchAll은 필요가 없으니,, 아마 센터 상세 조회 api가 저 쿼리 사용하는 함수를 같이 쓰고 있어서 문제일 텐데 일단 테스트용이니 잠깐 바꿔줘자.
@Override public Center findById(Long id) { return em.find(Center.class, id); }
Java
복사
3.8초로 줄었지만 그래도 여전히 오래 걸린다.
조금 더 최적화해서 in절 사용하게 해보자..
@Override public List<Center> findByIds(List<Long> ids) { TypedQuery<Center> query = em.createQuery("SELECT c FROM Center c WHERE c.id IN :ids", Center.class); query.setParameter("ids", ids); return query.getResultList(); }
Java
복사
1번 방식) fetch join
0.734초
2번 방식) 일반 select
70개 가져오는 데 0.3초 + 4.1초
in절 사용할 땐 70개 가져오는 데 0.3초 + 0.27초 = 0.57초
참고

(3) 후속 테스트

1번 방식과 2번 방식의 차이가 애매한데, 좀 더 많은 양의 데이터로 좀 더 정교하게 비교해보기로 했다.
일단 가져오는 데이터 필드를 똑같게 만들어줬고 데이터 양을 둘다 똑같이 902개로 늘렸다.
순서 보장
1번 방식) fetch join (select in)
0.688초 ~ 0.9xxx초
기존 쿼리
2번 방식) select in
0.554초 + 0.322초 = 0.8xxx초
기존 쿼리
select in 쿼리
여전히 비슷하다. 더이상 데이터 늘리려면 query string 자리가 부족해지기에 여기까지만 테스트하고 정리했다.

(4) 결론

1번 방식을 사용하기로 했다. 직적 쿼리 짤 필요 없이 같은 성능으로 간단히 최적화해주기 때문이다.

N+1문제와 batch

정의) 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오는 현상
특징) batch를 사용해 fetch join을 하면 in 절로 select 해와 N+1 문제를 해결할 수 있다.