주식 그래프를 그려주기 위해서 10만개의 데이터를 GET 요청을 보내야 한다.
이 데이터를 조회하기 위한 기존 쿼리로는 이렇게 활용하고 있었다.
<select id="findLast3BusinessDaysByStockId" resultType="StockPriceRaw">
SELECT spr.price_id, spr.stock_id, spr.price, spr.recorded_at
FROM STOCK_PRICE_RAWS spr
JOIN (
SELECT DATE(recorded_at) AS biz_date
FROM STOCK_PRICE_RAWS
WHERE stock_id = #{stockId}
AND DAYOFWEEK(recorded_at) BETWEEN 2 AND 6
GROUP BY DATE(recorded_at)
ORDER BY biz_date DESC
LIMIT 3
) recent ON DATE(spr.recorded_at) = recent.biz_date
WHERE spr.stock_id = #{stockId}
ORDER BY spr.recorded_at DESC
</select>
처음에는 recorded_at과 stock_id에 복합인덱스를 달아서 해결하려고 하였다.
하지만 Date 함수를 사용한 쿼리는 인덱스 처리가 안되었고 아직까지도 API 응답 속도가 1초가 넘었다..
Date를 Mysql이 아닌 백엔드 서비스 단에서 처리하고 복합인덱스를 100% 활용할 수 있도록 쿼리를 수정하였다.
EXPLAIN
SELECT price_id, stock_id, price, recorded_at
FROM STOCK_PRICE_RAWS
WHERE stock_id = 4
AND (
(recorded_at >= '2025-09-24 00:00:00' AND recorded_at < '2025-09-25 00:00:00')
OR (recorded_at >= '2025-09-23 00:00:00' AND recorded_at < '2025-09-24 00:00:00')
OR (recorded_at >= '2025-09-22 00:00:00' AND recorded_at < '2025-09-23 00:00:00')
)
ORDER BY recorded_at DESC;
그렇게 복합 인덱스를 100프로 활용할 수 있게 되었고, 불필요한 join이 사라지게 되면서 API 응답속도를 200ms까지 줄일 수 있었다.
10만 건의 주식 데이터를 조회하는 API의 응답 속도가 1초 이상 걸리는 성능 문제를 겪었다.
핵심 원인:
DATE()
함수 사용으로 인한 인덱스 미활용해결 방법:
recorded_at >= '2025-09-24 00:00:00' AND recorded_at < '2025-09-25 00:00:00'
형태로 범위 검색성능 개선 결과:
(stock_id, recorded_at)
100% 활용이번 경험을 통해 인덱스 설계만큼이나 쿼리 작성 방식이 중요하다는 것을 배웠다. 함수를 사용하거나 복잡한 JOIN을 남발하면 인덱스가 있어도 성능을 제대로 활용할 수 없다. 데이터베이스에 모든 로직을 맡기기보다는, 애플리케이션 레이어에서 처리할 수 있는 부분은 분리하는 것이 성능 최적화의 핵심임을 알았습니다.