스칼라 쿼리와 쿼리 최적화 경험
관리자 페이지 대시보드 관련해서 스칼라 쿼리를 수정해서 성능향상했던 경험을 이야기 해보고자 한다
처음 SQL 문을 배울때는 그냥 모든게 서브쿼리라고 생각했지만 서브쿼리의 위치에 따라 그 종류는 3가지로 나눌 수 있다.
1. 스칼라쿼리: Select 절에서 사용
2. 인라인뷰: From 절에서 사용
3. 단일 행, 다중 행, 다중 열 서브쿼리: 기타 조건절에서 사용
그 중 오늘 이야기할 경험은 스칼라 쿼리에 대한 것이다.
스칼라 쿼리는 아래와 같이 Select 절에서 사용되는 서브쿼리이다
SELECT (SELECT COUNT(*) FROM TABLE A WHERE A.MONTH = M.MONTH AND A.COLUMN = 'SOMETHING1') <--
, (SELECT COUNT(*) FROM TABLE A WHERE A.MONTH = M.MONTH AND A.COLUMN = 'SOMETHING2') <-- 스칼라쿼리
, (SELECT COUNT(*) FROM TABLE A WHERE A.MONTH = M.MONTH AND A.COLUMN = 'SOMETHING3') <--
FROM MONTH_DATA M;
나의 경험에 대한 이야기를 시작해보자면 그날은 운영서버에 배포가 있던 날이었다.
배포가 끝난 후 운영서버 이곳저곳을 확인하던 중 대시보드의 초기 로딩속도가 이상할정도로 느린것이 보였다.
해당 페이지 진입시 호출되는 API 쿼리를 확인한 결과 위의 쿼리와 비슷한 형태였다.
(DB 는 참고로 MYSQL을 사용중이었다.)
월별 특정 데이터에 대한 COUNT 내용을 각 스칼라 쿼리에서 뽑아 내는 상황이었다. 해당 쿼리를 따로 DB 툴을 사용해서 실행해본 결과 3.XXs의 실행 속도를 보이고 있었다. 최초 작성하였던 팀원은 데이터가 충분하지 않던 개발서버 기준 데이터로 확인하다 보니 쿼리의 속도에 대한 문제를 확인 못한것 같았다. 운영서버의 경우 테이블 A에 양이 꽤나 쌓였고 이부분에서 속도가 느려진것 같다는 생각이 들었다.
여러개의 스칼라 쿼리가 이어져 있었기에 관련한 속도 최적화에 대한 내용들을 찾아보았다. 그 내용들을 대략 정리하자면 아래와 같다.
1. 스칼라 쿼리는 캐싱효과가 있어 동일한 값을 반환하는 상황에서는 성능 향상의 가능성이 있다.
2. 스칼라 서브쿼리가 조회하는 쿼리의 조건이 변경되어 캐싱되지 않으면 성능이 떨어질 가능성이 있다.
3. 스칼라 서브쿼리의 조회 데이터가 많다면 성능이 떨어질 수 있다.
당시 쿼리를 대략적으로 표현했지만 최종 그결과는 최근 7개월간의 결과를 보여주는 데이터 조회쿼리였기에 총 7 row만 조회되고 있었기에 캐싱 기능의 필요성은 낮았다. 또한 조회되는 row 마다 매번 다른값(월별)을 조회해야했기에 캐싱이 되지도 않았을 것이며, TABLE A 자체의 데이터가 쌓여있다보니 스칼라 서브쿼리 조회 자체에 시간이 많이 소요되었을 것이다. 따라서 스칼라 서브쿼리의 성능 장점은 살릴수 없었고 단점만 부각되는 상황이었다.
해당 쿼리를 최적화한 형태는 대략 아래와 같다.
SELECT COUNT(CASE WHEN A.COLUMN = "SOMETHING1" THEN 1 END)
, COUNT(CASE WHEN A.COLUMN = "SOMETHING2" THEN 1 END)
, COUNT(CASE WHEN A.COLUMN = "SOMETHING3" THEN 1 END)
FROM MONTH_DATA M
JOIN M.MONTH = A.MONTH
GROUP BY M.MONTH
우선 TABLE A에 대한 부분을 스칼라 서브쿼리를 조인 서브쿼리로 변경하여 TABLE A 에 전체 테이블 조회의 횟수를 줄였다. 변경 전 쿼리의 경우 TABLE A 에 대한 전체조회를 각 스칼라쿼리가 실행될 때마다 했을 것이다. 그러나 조인을 통하여 그 횟수를 줄였다. 그리고 COUNT 함수의 경우 내부의 값이 NULL 이 아닌 ROW 의 개수를 COUNT 하기때문에 위와 같이 작성할 경우 조건에 해당하지 않은경우 NULL 이 되어 COUNT 되지 않는다는 사실을 이용하여 월별 데이터를 조회하였다.
위와 같이 쿼리를 변경한 이후 해당 데이터의 조회속도 DB툴 기준으로 0.03s~0.04s 로 향상되었다. 초기 쿼리가 3초대였던 것을 생각하면 거의 100배에 가까운 속도를 향상시켰다!
해당 경험을 통하여 스칼라 쿼리에 대한 정보들을 찾아보며 쿼리의 실행에 대한 많은 고찰을 할 수 있었다.
만일 스칼라쿼리를 쓰고있으며 전체 ROW수가 적다면 나와 같은 스칼라쿼리를 쓰지 않고 JOIN 문을 이용하여 속도향상을 시도해보길 추천한다. 반면에 전체실행쿼리의 ROW 수가 굉장히 많고 스칼라 쿼리 각각의 값이 고정되는 형태라면 전체쿼리를 실행하는동안 스칼라쿼리의 캐싱기능에 도움을 많이 받을 수 있을 것 생각된다. 그런 경우엔 스칼라쿼리를 적극 사용해야할 것이다.