회사 서비스의 특정 API 호출 시 응답 시간이 12초 이상 소요되는 문제가 발생했다. 고객들이 자주 조회하는 페이지라서 개선이 시급해보였다. 우선 해당 API의 쿼리 문제가 있는지 알아보기 위해 실행 계획을 분석해보기로 했다.
해당 API에서는 총 3개의 쿼리가 SELECT 문으로 조회되고 있었다. 그 중 어떤 쿼리의 응답 속도에 문제가 있는지 알아보기 위해 우선 회사에서 사용 중인 AWS RDS 성능 모니터링 페이지를 확인해봤다.
최근 1시간 동안 상위 SQL을 확인해보니 3개 중 하나의 쿼리가 특히 조회하는 데 오랜 시간이 걸리는 것으로 나타났다. 해당 쿼리의 실행 계획을 분석해보기로 했다.
MySQL 실행 계획을 확인하려면 쿼리 문 앞에 EXPLAIN을 붙여주면 된다. 그럼 MySQL 옵티마이저가 해당 쿼리를 가장 효율적으로 실행할 수 있는 실행 계획을 짜서 표 형태로 보여준다.
실행 계획을 분석해보는 게 처음이라 먼저 실행 계획 테이블(query plan table)을 보는 방법부터 익혀야 했다.
위 내용을 바탕으로 해당 쿼리의 실행 계획을 확인해본 결과, 일부 실행계획에 문제가 있는 것으로 예상이 되었다.
id 값에 기반해 해당하는 SELECT 쿼리를 찾아보니 다음과 같았다. (컬럼과 테이블 명은 임의로 수정)
WHERE idx IN (
SELECT
MAX(idx) AS idx
FROM table_name
WHERE is_deleted = 'N'
AND d = 1
GROUP BY a, b, c
HAVING c IN (1, 2, 3, 4)
)
GROUP BY와 HAVING 절로 인해 실행 속도가 느려진 것으로 보였다.
쿼리를 확인해보니 HAVING 절을 사용한 부분이 성능을 저하시키는 것 같아 WHERE 절로 바꿔봤는데 결과가 동일하게 나타났다. (모든 HAVING 절을 WHERE 절로 대체할 수 있는 것은 아니다. 해당 쿼리의 경우 WHERE 절로 변경해도 무방했기 때문에 변경한 것)
그래서 다음과 같이 HAVING 절을 삭제하고 WHERE 절의 AND 조건으로 변경했다.
WHERE idx IN (
SELECT
MAX(idx) AS idx
FROM table_name
WHERE is_deleted = 'N'
AND d = 1
AND c IN (1, 2, 3, 4)
GROUP BY a, b, c
)
응답 속도가 약 12s → 약 84ms로 개선되었다.
결과적으로 HAVING 절의 컬림이 WHERE 절로 가면서 인덱스를 타게 되었다.
다른 방법으로는 GROUP BY 절에 해당하는 컬럼을 인덱스로 만드는 방법도 있을 것 같다.
첫 실행 계획 분석 완료!