MySQL 쿼리 최적화: 테이블 스캔, 인덱스 스캔, 옵티마이저, 인덱스 힌트, EXPLAIN ANALYZE

2025. 2. 25. 17:56· 백엔드
목차
  1. 1. 테이블 스캔 vs 인덱스 스캔
  2. 테이블 스캔 (Full Table Scan)
  3. 인덱스 스캔 (Index Scan)
  4. 요약
  5. 2. MySQL 인덱스 힌트(Index Hints)란?
  6. 1-1. 인덱스 힌트 종류
  7. 2. EXPLAIN ANALYZE로 실행 계획 분석
  8. 2-1. EXPLAIN, EXPLAIN ANALYZE란?

MySQL 쿼리 최적화: 테이블 스캔, 인덱스 스캔, 옵티마이저, 인덱스 힌트, EXPLAIN ANALYZE

MySQL에서 쿼리 성능 최적화를 위해 가장 중요한 요소 중 하나는 옵티마이저(Optimizer)가 선택하는 실행 계획(Execution Plan) 이다.
MySQL의 옵티마이저가 올바른 인덱스를 선택하지 못할 경우, 인덱스 힌트(Index Hints)를 사용하여 최적의 실행 계획을 유도할 수 있다.

즉, EXPLAIN ANALYZE를 활용하여 옵티마이저의 실행 계획을 분석하고, 인덱스 힌트를 사용한다.

1. 테이블 스캔 vs 인덱스 스캔

테이블 스캔 (Full Table Scan)

  • 정의:
    인덱스를 사용하지 않고 테이블의 모든 행(데이터 페이지)을 순차적으로 읽어오는 방식이다.

  • 특징:

    • EXPLAIN 결과의 “type” 컬럼에 ALL로 표시된다.
    • 적절한 인덱스가 없거나, 반환할 행 수가 테이블 전체의 큰 비율일 때 옵티마이저가 선택한다.
    • 순차적 읽기(시퀀셜 I/O)로 디스크 접근은 효율적일 수 있지만, 불필요한 행도 모두 읽기 때문에 비용이 높다.

인덱스 스캔 (Index Scan)

  • 정의:
    B-Tree 인덱스를 사용하여 조건에 맞는 인덱스 리프 노드를 탐색한 후, 해당 값에 대응하는 행(주로 PK)를 찾아 데이터를 조회하는 방식이다.
    커버링 인덱스(covering index): 만약 쿼리에서 필요한 모든 컬럼이 인덱스 내에 포함되어 있다면, 실제 테이블 데이터까지 접근하지 않고 인덱스만으로 처리할 수 있다.

  • 특징:

    • EXPLAIN 결과에서 range, ref, eq_ref 등으로 표시된다.
    • 조건에 맞는 행이 테이블 전체에 비해 상대적으로 적은 경우(예: 5~25% 미만) 비용이 낮아진다.
    • 인덱스에 포함되지 않은 컬럼이 필요하면 인덱스에서 찾은 PK로 추가 랜덤 I/O가 발생할 수 있다. (비 클러스터 형 인덱스 사용 시)

요약

  • 테이블 스캔:
    전체 테이블 데이터를 순차적으로 읽어 “ALL” 방식으로 처리하며, 인덱스가 없거나 반환 행이 많을 때 선택된다.

  • 인덱스 스캔:
    인덱스를 통해 조건에 맞는 행의 위치를 빠르게 찾고(종종 커버링 인덱스를 활용), “range”, “ref”, “eq_ref” 방식으로 처리되어 반환 행이 적을 때 효율적이다.

MySQL 옵티마이저는 데이터 분포, 인덱스 커버리지, 예상 반환 행 수 등을 고려하여 두 방식을 자동으로 선택한다.

출처:
dev.mysql.com
velog.io

2. MySQL 인덱스 힌트(Index Hints)란?

MySQL 옵티마이저는 기본적으로 쿼리 실행 시 가장 효율적인 인덱스를 자동으로 선택한다.
하지만, 때로는 옵티마이저의 선택이 최적이 아닐 때 인덱스 힌트(Index Hints) 를 사용하여 원하는 인덱스를 사용하거나 무시하도록 지시할 수 있다.

MySQL에서는 기본적으로 옵티마이저(Optimizer)가 자동으로 최적의 인덱스를 선택한다.
하지만, 경우에 따라 MySQL이 적절한 인덱스를 사용하지 않고, 풀 테이블 스캔을 수행할 수도 있다.

👉 이럴 때 특정 인덱스를 강제로 사용하도록 하는 방법이 FORCE INDEX, USE INDEX, IGNORE INDEX 이다.

1-1. 인덱스 힌트 종류

인덱스 힌트 설명 예제
USE INDEX (index_list) 특정 인덱스를 사용하도록 권장 SELECT * FROM orders USE INDEX (idx_customer_id) WHERE customer_id = 1001;
FORCE INDEX (index_list) 특정 인덱스를 반드시 사용 SELECT * FROM orders FORCE INDEX (idx_customer_id) WHERE customer_id = 1001;
IGNORE INDEX (index_list) 특정 인덱스를 무시 SELECT * FROM orders IGNORE INDEX (idx_customer_id) WHERE customer_id = 1001;

🚨 주의:

  • USE INDEX는 옵티마이저에게 특정 인덱스를 "사용하라"고 요청하지만, 필수는 아님.
  • FORCE INDEX는 옵티마이저가 무조건 해당 인덱스를 사용하도록 강제함.
  • IGNORE INDEX는 특정 인덱스를 무시하고 다른 인덱스를 사용하거나 풀 테이블 스캔을 유도함.

📌 사용 시점

  • 옵티마이저가 적절한 인덱스를 사용하지 않을 경우
  • 실행 계획을 분석한 후 특정 인덱스를 강제 적용해야 할 필요가 있을 경우

2. EXPLAIN ANALYZE로 실행 계획 분석

2-1. EXPLAIN, EXPLAIN ANALYZE란?

MySQL 8.0부터 도입된 EXPLAIN ANALYZE를 사용하면 실제 실행 계획과 소요 시간, I/O 통계 등을 확인할 수 있다.

✅ 사용 예제

EXPLAIN SELECT * FROM orders WHERE customer_id = 1001;
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 1001;

✅ 실행 결과 예시
EXPLAIN 문

id select_type table type possible_keys key key_len ref rows filtered execution time
1 SIMPLE orders ref idx_customer_id idx_customer_id 4 const 10 100.00 0.005 sec
![[무제 1-20250225143926508.webp]]
EXPLAIN ANALYZE 문

📌 분석 방법

  • key: 사용된 인덱스 확인 (idx_customer_id 사용)
  • rows: 조회된 예상 행 수 (10개)
  • execution time: 실제 실행 시간 (0.005초)
  • type: ALL(풀 스캔) → ref → range → const → system 순으로 효율적

✅ 활용 방법

  • EXPLAIN ANALYZE 실행 후 인덱스 미사용 시 → USE INDEX / FORCE INDEX 고려
  • rows 값이 지나치게 크면 → 인덱스 추가 또는 조건 최적화 필요

참고 자료:

  • MySQL 8.0 Reference Manual – Index Hints
  • MySQL 8.0 Reference Manual – EXPLAIN ANALYZE

'백엔드' 카테고리의 다른 글

[Spring JPA] 1. JPA 이야기 (김영한 JPA 교재)  (0) 2025.03.05
JPA Entity(JPA 공식문서 정리 - 1)  (1) 2025.03.03
Included Columns(SQL-server)  (0) 2025.02.21
인덱스 조각화, 실행계획, 인덱스 scripts  (0) 2025.02.21
인덱스의 기초 - Page,Extent,Heap,Clustered Index(Sql-server)  (0) 2025.02.17
  1. 1. 테이블 스캔 vs 인덱스 스캔
  2. 테이블 스캔 (Full Table Scan)
  3. 인덱스 스캔 (Index Scan)
  4. 요약
  5. 2. MySQL 인덱스 힌트(Index Hints)란?
  6. 1-1. 인덱스 힌트 종류
  7. 2. EXPLAIN ANALYZE로 실행 계획 분석
  8. 2-1. EXPLAIN, EXPLAIN ANALYZE란?
'백엔드' 카테고리의 다른 글
  • [Spring JPA] 1. JPA 이야기 (김영한 JPA 교재)
  • JPA Entity(JPA 공식문서 정리 - 1)
  • Included Columns(SQL-server)
  • 인덱스 조각화, 실행계획, 인덱스 scripts
최현준 개발일기
최현준 개발일기
남이 이해하기 쉽도록 글을 쓰는 걸 좋아합니다. https://github.com/Hyeonjun0527
최현준 개발일기
최현준 개발일기
최현준 개발일기
전체
오늘
어제
  • 분류 전체보기 (30)
    • 프론트엔드 (0)
    • 알고리즘 (10)
    • 백엔드 (13)
      • 김영한 스프링 기본편 (4)
    • 분류하기 애매한 것들 (0)
    • ZERO-TO-ONE-프로젝트 (2)
    • 휴지통(추후 관리 및 삭제) (0)
      • JAVA (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 백준
  • 엔티티 매니저 팩토리
  • 실행 계획
  • 인덱스
  • JPA
  • included columns
  • sql-server
  • MySQL
  • PS
  • fill factor
  • DBMS
  • 인덱스 조각화
  • 알고리즘

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
최현준 개발일기
MySQL 쿼리 최적화: 테이블 스캔, 인덱스 스캔, 옵티마이저, 인덱스 힌트, EXPLAIN ANALYZE
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.