본문 바로가기
CS/Database

[MySQL] Query Cost가 높아지는 조건과 성능 개선 포인트

by clolee 2025. 4. 13.

✅ Query Cost가 높아지는 조건과 성능 개선 포인트


✅ 1. 쿼리 비용(Query Cost)이란?

  • MySQL 옵티마이저(optimizer)가 계산한 논리적 실행 비용
  • EXPLAIN 또는 EXPLAIN ANALYZE에서 cost=0.15..23.80 이런 식으로 나타남
  • 크다고 해서 항상 "나쁘다"는 뜻은 아님 → 상대적 판단 기준
  • 주로 다음 요소가 반영됨:
    • 디스크 I/O 비용 (풀 테이블 스캔, 정렬, 임시 테이블)
    • 읽는 행 수
    • 인덱스 사용 여부
    • 조인 순서
    • 반복 루프 횟수

✅ 2. 쿼리 비용이 높아지는 주요 조건 + 개선 방향

조건 성능 저하 원인 개선 포인트
1. type: ALL 테이블 전체 스캔 (풀스캔) 인덱스 추가, WHERE 조건 개선
2. key: NULL 인덱스 미사용 적절한 인덱스 생성
3. WHERE 절에 함수 사용 인덱스 범위 못 잡음 WHERE DATE(col)BETWEEN 사용
4. Using temporary 임시 테이블 생성 (GROUP BY 등) GROUP BY 대상에 인덱스 포함
5. Using filesort 정렬 성능 저하 (ORDER BY) 정렬 대상 인덱스 생성
6. JOIN 순서 잘못됨 큰 테이블 먼저 조인 → I/O 증가 작은 테이블 먼저 조인
7. 하위 쿼리 중첩 반복 실행 (비효율) JOIN 또는 CTE로 구조 재설계
8. OR 조건 복수 컬럼 옵티마이저 인덱스 사용 못함 UNION으로 분리
9. LIMIT 없이 대량 조회 메모리 + 네트워크 부하 LIMIT + 페이지네이션 적용
10. 서브쿼리에서 인덱스 미사용 서브쿼리 내부 풀스캔 발생 인덱스 조정 + EXISTS로 변경 고려

✅ 3. 성능 최적화를 위한 중점 개선 항목

🔸 1) 인덱스가 제대로 활용되는지 확인

  • EXPLAINkeyNULL이면 성능 병목
  • WHERE, JOIN, ORDER BY, GROUP BY에 쓰이는 컬럼에 인덱스 설정
  • 복합 인덱스 고려: 자주 함께 쓰이는 컬럼 순서대로

✅ 인덱스 예:

CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);

🔸 2) WHERE 조건에 함수나 계산 피하기

  • WHERE YEAR(created_at) = 2023
  • WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31'

👉 함수 사용 시 인덱스가 무력화됨


🔸 3) ORDER BY / GROUP BY 최적화

  • EXPLAINExtraUsing filesort, Using temporary 나오면 정렬/집계 비용 ↑
  • 인덱스가 ORDER/GROUP 대상 컬럼 순서와 동일해야 함
-- 개선 전: 정렬 시 filesort 발생
SELECT * FROM posts ORDER BY created_at DESC;

-- 개선 후: 인덱스 생성
CREATE INDEX idx_posts_created ON posts(created_at DESC);

🔸 4) JOIN 순서, 방식 확인

  • EXPLAIN에서 큰 테이블부터 조인하거나, type: ALL이면 위험
  • 작은 테이블 먼저 조인되도록 쿼리 구조/인덱스를 조정
-- 예: 작은 테이블 먼저 필터링
SELECT * FROM small_table s
JOIN big_table b ON s.id = b.s_id
WHERE s.status = 'active';

🔸 5) 서브쿼리 vs JOIN vs CTE 비교

  • 서브쿼리는 행마다 반복 실행될 수 있어 비용 증가
  • 가능하면 JOIN + GROUP BY로 변경
  • MySQL 8 이상이면 CTE(WITH절)로 리팩토링 고려

✅ 4. 실무 기준 성능 분석 흐름

  1. EXPLAIN으로 실행 계획 확인
  2. type = ALL, key = NULL, Extra: Using filesort/temporary 있는지 확인
  3. 🔍 스캔 행 수(rows)가 수십만 이상인지 확인
  4. EXPLAIN ANALYZE로 실제 수행 시간 측정
  5. ⚙️ 인덱스 최적화, 조인 순서 재구성
  6. 🚀 쿼리 리팩토링 후 다시 EXPLAIN 비교

✅ 예시: BEFORE vs AFTER

❌ BEFORE (문제 있음)

EXPLAIN SELECT * FROM orders WHERE LEFT(order_date, 7) = '2024-03';
-- type: ALL, key: NULL, rows: 1,000,000

✅ AFTER (개선)

SELECT * FROM orders
WHERE order_date BETWEEN '2024-03-01' AND '2024-03-31';
-- type: range, key: idx_order_date, rows: 12,000

✅ 정리: 성능을 위해 꼭 봐야 할 항목 5선

항목 중점 체크
type ALLref, range 이상으로 개선
key NULL이면 인덱스 미사용, 인덱스 설계 필요
rows 수백만 이상이면 개선 필요
Extra Using filesort / Using temporary → 정렬/임시 테이블 사용중
EXPLAIN ANALYZE의 actual time 실제 응답 시간 기준으로 체감 성능 판단

댓글