SQL 개선을 통한 API 응답시간 단축 작업

Timo·2022년 7월 29일
0

배경

홈 화면 및 검색에 사용하는 수업 조회 API의 응답시간이 2초에서 3초로 상당히 오래 걸리는 상황이었습니다. API의 이상적인 응답시간은 300ms 내외이기 때문에 응답시간이 긴 이유를 확인하고, 이를 개선할 필요가 있었습니다.

문제 상황

홈 화면에서 호출하는 수업 목록 조회 API의 SQL을 확인해 보았으며 아래와 같은 문제들이 원인으로 파악되었습니다.

  • SELECT 절에 사용된 다수의 스칼라 서브쿼리(Scalar Subquery)
  • SELECT 쿼리와 COUNT 쿼리가 동일한 FROM절을 재사용하여 카운트 쿼리에서 불필요한 조인이 포함됨
  • 수업에 사용하는 부가 정보(ex. 뱃지)를 조건 없이 전체 다 조회하여 수업 검색 결과와 비교하여 병합

해결 방법

원인으로 파악된 SQL 문제들을 개선하는 것으로 방향을 정했습니다.

  • 불필요한 서브쿼리 제거, SELECT절의 스칼라 서브쿼리를 JOIN으로 변경
  • COUNT 쿼리의 FROM 절을 필요한 JOIN만 사용하도록 별도로 분리
  • 검색 결과에 대한 수업에 해당하는 뱃지 정보를 조회할 수 있도록 조건 추가

해결 과정

1) 서브쿼리를 JOIN으로 변경

서브쿼리는 아래와 같은 이유 때문에 성능에 문제를 줄 수 있습니다.

  1. 서브쿼리는 SQL 내부에서 만들어지는 일시적인 테이블이기 때문에 연산 비용이 추가된다.
  2. 서브쿼리에 아우터 쿼리의 컬럼과 비교하는 조건문이 있다면 컬럼값을 비교할 때마다 내부 쿼리가 실행된다.
  3. 서브쿼리는 가상의 테이블이기 때문에 메타 정보가 없다. 따라서 검색 시 인덱스를 사용하지 않는다.

조인 역시 가상 테이블을 만든다는 점은 동일하지만 FROM 점을 실행할 때에만 한번 생성하게 되므로 성능 부담이 적을 것이라 판단했습니다.
또한 WHERE절을 수행할 때 컬럼 정보를 통해 인덱스를 사용할 것이므로 서브쿼리보다 빠른 검색 속도를 가질 것이라 생각했습니다.

2) COUNT 쿼리 분리

SELECT 쿼리의 FROM 절과 COUNT 쿼리의 FROM 절은 같이 쓰기에 사실 무리(?)가 있었습니다. 아마도 코드를 재사용한다는 명분하에 같이 사용했던 걸로 추측되지만, SELECT 절에서 필요한 컬럼을 추출하기 위해 작성된 조인이 많았습니다. 따라서 이러한 불필요한 조인들을 제거하고 카운트 쿼리만의 FROM 절을 만들었습니다.

3) 뱃지 조회 시 조건 추가

기존에는 뱃지 조회가 수업 목록 조회 결과에 영향을 받지 않았으므로 먼저 조회되었습니다. 이를 수업 목록 조회 이후로 옮겨 수업 목록 결과를 뱃지 조회 WHERE절, IN 조건에 넣어 해당하는 뱃지 로우만 가져올 수 있도록 I/O 비용을 줄였습니다.

결과

포스트맨의 Test Runner 를 사용해 20회씩 측정한 결과
개선 후 평균 335ms 응답시간을 가질 수 있게 되었습니다.

느낀 점

사실 기발하고 거창한 방법이 아닌 기본적인 것들을 고쳐나가면서 문제 상황을 해결한 경험입니다. 이 경험을 통해 다시 한번 '어떻게?'보다는 '왜?'를 고민하는 것이 먼저여야 한다는 것을 느끼게 되었습니다. 문제의 원인을 정확하게 파악했기 때문에 간단한 개선만으로 효과적인 결과를 얻을 수 있었기 때문입니다.

profile
나는 매일 성장하는 사람

0개의 댓글