티스토리 뷰
실행계획(Execution Plan) 보는 방법, 느린 쿼리 잡는 핵심
SQL 성능 문제를 공부하다 보면 반드시 만나게 되는 것이 바로 실행계획(Execution Plan)입니다. 그런데 처음 보면 type, rows, key, Extra, cost 같은 정보가 한꺼번에 나와서 어디부터 봐야 할지 막막한 경우가 많습니다.
하지만 실행계획은 생각보다 어렵게 접근할 필요가 없습니다. 핵심은 단 하나입니다.
이번 글에서는 실행계획이 무엇인지, 왜 봐야 하는지, 어디부터 읽어야 하는지, 그리고 느린 쿼리를 볼 때 어떤 항목을 먼저 체크하면 좋은지까지 초보 눈높이에서 정리해보겠습니다.
예를 들어 아래 쿼리를 보겠습니다.
SELECT *
FROM users
WHERE email = 'ace@example.com';
겉으로 보기엔 단순한 조회입니다. 하지만 실제로는 DB가 아래 두 방식 중 하나를 선택할 수 있습니다.
- 인덱스를 사용해서 빠르게 찾기
- 테이블 전체를 훑기
둘 다 같은 SQL이지만 성능 차이는 매우 클 수 있습니다.
즉, SQL 문장만 읽어서는 부족하고, DB가 실제로 어떤 실행 전략을 고른 것인지를 봐야 합니다. 그 정보가 바로 실행계획입니다.
대표적으로는 EXPLAIN을 사용합니다.
EXPLAIN
SELECT *
FROM users
WHERE email = 'ace@example.com';
DBMS에 따라 출력 형식은 다르지만, 보통 아래 같은 정보를 보게 됩니다.
- 어떤 테이블을 먼저 읽는지
- 인덱스를 사용하는지
- 얼마나 많은 행을 읽을 것으로 예상하는지
- 어떤 방식으로 조인하는지
- 추가 정렬이나 임시 테이블이 필요한지
즉, 실행계획은 단순 결과가 아니라 처리 과정의 설계도입니다.
초보가 실행계획을 볼 때는 아래 4가지를 먼저 보면 좋습니다.
- 인덱스를 탔는가?
- 예상 읽기 행 수(rows)가 많은가?
- 조인 순서가 이상하지 않은가?
- 추가 정렬/임시 테이블이 생기지 않았는가?
처음부터 모든 속성을 다 해석하려고 하면 오히려 더 어렵습니다. 성능 문제를 잡을 때는 어디서 많이 읽고, 어디서 많이 버리고, 어디서 추가 작업이 생기는지를 먼저 보는 것이 중요합니다.
MySQL 스타일 기준으로 많이 보는 항목을 예로 들면 아래와 같습니다.
| 항목 | 의미 |
|---|---|
| type | 어떤 방식으로 테이블을 읽는지 |
| key | 실제로 사용한 인덱스 |
| rows | 읽을 것으로 예상하는 행 수 |
| Extra | 추가 작업 정보 (Using filesort, Using temporary 등) |
특히 초보 단계에서는 아래처럼 기억하면 충분합니다.
key가 비어 있으면 인덱스를 못 탔을 가능성rows가 너무 크면 많은 데이터를 읽는 중일 가능성Extra에Using filesort,Using temporary가 보이면 추가 비용이 있을 가능성
1) Full Table Scan
인덱스를 타지 못해서 테이블 전체를 읽는 경우입니다.
2) 너무 많은 rows
인덱스를 타더라도 읽는 행 수가 너무 많으면 느릴 수 있습니다.
3) 불필요한 정렬
ORDER BY 때문에 추가 정렬 비용이 크게 발생할 수 있습니다.
4) 임시 테이블 사용
그룹화나 정렬 과정에서 임시 작업이 발생할 수 있습니다.
5) 비효율적인 조인 순서
작은 테이블부터 잘 거르지 못하고 큰 데이터를 먼저 읽으면 부담이 커질 수 있습니다.
실행계획을 볼 때는 아래 순서로 접근하면 좋습니다.
- 어떤 테이블을 먼저 읽는지 본다.
- 인덱스를 썼는지 확인한다.
- 예상 rows가 큰지 본다.
- Extra에서 filesort/temporary가 있는지 본다.
- WHERE 조건과 JOIN 조건이 인덱스를 탈 수 있는 구조인지 다시 본다.
즉, 실행계획은 단순히 “좋다/나쁘다”를 외우는 것이 아니라, DB가 어떤 선택을 했는지 따라가 보는 과정입니다.
실행계획을 보고 보통 아래 같은 개선 방향을 찾습니다.
- 적절한 인덱스 추가 또는 수정
- 불필요한
SELECT *제거 - WHERE 조건 단순화
- 함수 사용 때문에 인덱스를 못 타는 부분 개선
- ORDER BY / GROUP BY 구조 재검토
- JOIN 순서나 JOIN 대상 축소
예를 들어 인덱스 컬럼에 함수를 걸면 DB가 인덱스를 활용하지 못할 수 있습니다.
SELECT *
FROM users
WHERE DATE(created_at) = '2026-03-24';
이런 경우는 보통 범위 조건으로 바꿔보는 것이 더 유리할 수 있습니다.
SELECT *
FROM users
WHERE created_at >= '2026-03-24 00:00:00'
AND created_at < '2026-03-25 00:00:00';
1) 인덱스만 쓰면 무조건 빠르다?
아닙니다. 인덱스를 타더라도 rows가 많으면 느릴 수 있습니다.
2) rows는 실제 읽은 행 수다?
항상 그렇진 않습니다. 보통 예상치 또는 추정치입니다.
3) 실행계획만 보면 끝이다?
아닙니다. 실제 실행 시간, 데이터 분포, 인덱스 상태도 함께 봐야 합니다.
4) filesort가 보이면 무조건 틀렸다?
아닙니다. 다만 비용이 커질 수 있어 확인이 필요하다는 뜻에 가깝습니다.
초보 단계에서는 우선 이렇게 기억하면 충분합니다.
실행계획은 DB가 쿼리를 실제로 어떻게 처리하는지 보여주는 지도이고, 느린 쿼리를 잡을 때 가장 먼저 열어봐야 하는 도구입니다.
'IT > SQL·DB' 카테고리의 다른 글
| 트랜잭션(Transaction)이란? ACID와 격리수준까지 쉽게 이해하기 (0) | 2026.03.25 |
|---|---|
| 인덱스란 무엇인가? 초보도 이해하는 원리 + 성능 차이 (0) | 2026.03.25 |
| 데이터베이스 정규화 vs 반정규화, 언제 써야 할까? (0) | 2026.03.25 |
| 데이터베이스 Key 종류 총정리, 기본키·외래키·후보키·슈퍼키 쉽게 이해하기 (0) | 2026.03.25 |
| SQL JOIN 완벽 정리, INNER JOIN·LEFT JOIN·RIGHT JOIN 차이 한 번에 이해하기 (0) | 2026.03.24 |

