실행 계획(EXPLAIN) 읽는 법

한 줄 정의

실행 계획은 쿼리를 DB가 어떻게 실행할지 보여주는 분석 결과로, 인덱스 사용 여부, 접근 방식, 예상 비용을 확인할 수 있다.

실무에서 왜 중요한가

실행 계획을 읽지 못하면 다음 상황에서 원인을 찾을 수 없다.

  • 인덱스를 걸었는데 쿼리가 여전히 느리다.
  • 같은 테이블인데 쿼리마다 성능이 크게 다르다.
  • 어떤 인덱스가 실제로 사용되는지 모른다.
  • 쿼리 튜닝을 “감”으로 한다.

EXPLAIN 사용법

EXPLAIN SELECT * FROM orders WHERE user_id = 1 AND status = 'COMPLETED';
+----+------+-------+------+----------+-------------+
| id | type | table | key  | rows     | Extra       |
+----+------+-------+------+----------+-------------+
|  1 | ref  | orders| idx_user_status | 15 | Using where |
+----+------+-------+------+----------+-------------+

핵심 확인 항목

type (접근 방식)

성능에 가장 큰 영향을 준다. 위에서 아래로 갈수록 느리다.

type의미성능
constPK 또는 UNIQUE로 1건 조회최고
eq_refJOIN에서 PK/UNIQUE로 1건 매칭매우 좋음
ref인덱스로 여러 건 조회좋음
range인덱스 범위 스캔 (BETWEEN, >, <)보통
index인덱스 전체 스캔 (Full Index Scan)느림
ALL테이블 전체 스캔 (Full Table Scan)최악

ALL이 나오면 반드시 인덱스 추가를 검토한다.

key

실제로 사용된 인덱스 이름이다. NULL이면 인덱스를 사용하지 않은 것이다.

rows

DB가 예상하는 스캔 행 수다. 실제 값이 아니라 통계 기반 추정치다. 값이 클수록 비용이 높다.

Extra

의미
Using index커버링 인덱스 (테이블 접근 없음)
Using whereWHERE 조건으로 필터링
Using filesort인덱스로 정렬 불가, 별도 정렬 수행
Using temporary임시 테이블 사용 (GROUP BY, DISTINCT 등)

Using filesortUsing temporary가 함께 나오면 성능 개선이 필요하다.

실무 분석 예시

-- 느린 쿼리
EXPLAIN SELECT * FROM orders
WHERE status = 'COMPLETED'
ORDER BY created_at DESC
LIMIT 20;
type: ALL | key: NULL | rows: 500000 | Extra: Using where; Using filesort

문제: Full Table Scan + 별도 정렬

-- 인덱스 추가 후
CREATE INDEX idx_status_created ON orders (status, created_at);
 
EXPLAIN SELECT * FROM orders
WHERE status = 'COMPLETED'
ORDER BY created_at DESC
LIMIT 20;
type: ref | key: idx_status_created | rows: 20 | Extra: Using where; Backward index scan

개선: 인덱스로 조건 필터 + 정렬까지 처리. Backward index scan은 인덱스를 역순으로 스캔하는 것으로, DESC 정렬에서도 인덱스를 활용하고 있다는 의미다.

EXPLAIN ANALYZE (MySQL 8.0+)

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 1;

EXPLAIN은 예상치이고, EXPLAIN ANALYZE실제 실행 결과를 보여준다. 실제 행 수, 실행 시간, 루프 횟수를 확인할 수 있다.

자주 나는 실수

  • EXPLAIN 없이 인덱스를 추가하고 “빨라졌겠지”라고 가정한다.
  • type: ALL을 무시하고 넘어간다.
  • Using filesort가 나오는데 ORDER BY에 맞는 인덱스를 만들지 않는다.
  • rows 값이 예상보다 큰데 원인을 분석하지 않는다.
  • EXPLAIN의 rows가 추정치인 것을 모르고 정확한 값으로 믿는다.

핵심 요약

실행 계획은 EXPLAIN으로 확인하며, type, key, rows, Extra를 중심으로 읽습니다. type: ALL은 Full Table Scan이므로 인덱스 추가를 검토하고, Using filesort는 정렬용 인덱스가 필요합니다.

EXPLAIN ANALYZE(MySQL 8.0+)는 실제 실행 결과를 보여주므로 더 정확한 분석이 가능합니다. 쿼리 튜닝은 반드시 실행 계획을 먼저 확인하고 진행해야 합니다.

꼬리 질문

관련 문서