EXPLAIN으로 MySQL 쿼리 최적화하기: 느린 쿼리 분석 완벽 가이드

EXPLAIN 명령어로 MySQL 쿼리를 분석하고 최적화하는 실전 가이드. 실행 계획 읽는 법부터 병목 지점 찾기까지 상세히 설명합니다.

럿지 AI 팀
4분 읽기

EXPLAIN 완벽 가이드



EXPLAIN이란?



MySQL이 쿼리를 어떻게 실행할지 보여주는 명령어

**목적:** 느린 쿼리 원인 파악 및 최적화

기본 사용법



``sql
EXPLAIN SELECT * FROM orders WHERE member_id = 1;
`

EXPLAIN 결과 읽기



| 컬럼 | 의미 | 중요도 |
|------|------|--------|
| type | 조인 타입 | ⭐⭐⭐ |
| key | 사용된 인덱스 | ⭐⭐⭐ |
| rows | 검사할 행 수 | ⭐⭐⭐ |
| Extra | 추가 정보 | ⭐⭐ |

type 컬럼 (조인 타입)



성능 좋음 → 나쁨 순서:

1. const (최고)



`sql
EXPLAIN SELECT * FROM members WHERE member_id = 1;
-- type: const (PK/UNIQUE 단건 조회)
`

2. eq_ref (매우 좋음)



`sql
EXPLAIN
SELECT * FROM orders o
JOIN members m ON o.member_id = m.member_id;
-- type: eq_ref (조인 시 PK/UNIQUE 사용)
`

3. ref (좋음)



`sql
EXPLAIN SELECT * FROM orders WHERE member_id = 1;
-- type: ref (인덱스 사용, 여러 행 가능)
`

4. range (괜찮음)



`sql
EXPLAIN SELECT * FROM orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31';
-- type: range (범위 검색)
`

5. index (나쁨)



`sql
EXPLAIN SELECT member_id FROM orders;
-- type: index (인덱스 풀 스캔)
`

6. ALL (최악)



`sql
EXPLAIN SELECT * FROM orders WHERE total_amount > 10000;
-- type: ALL (테이블 풀 스캔!)
`

key 컬럼 (사용된 인덱스)



NULL = 인덱스 미사용



`sql
EXPLAIN SELECT * FROM orders WHERE total_amount > 10000;
-- key: NULL (인덱스 없음!)

-- 해결: 인덱스 추가
CREATE INDEX idx_amount ON orders(total_amount);

EXPLAIN SELECT * FROM orders WHERE total_amount > 10000;
-- key: idx_amount (인덱스 사용!)
`

rows 컬럼 (검사 행 수)



많을수록 느림



`sql
-- 나쁨
EXPLAIN SELECT * FROM orders;
-- rows: 1,000,000

-- 좋음
EXPLAIN SELECT * FROM orders WHERE order_id = 1;
-- rows: 1
`

Extra 컬럼



Using filesort (나쁨)



`sql
EXPLAIN SELECT * FROM orders ORDER BY total_amount;
-- Extra: Using filesort (정렬 비용 큼)

-- 해결: 인덱스 추가
CREATE INDEX idx_amount ON orders(total_amount);
`

Using temporary (나쁨)



`sql
EXPLAIN SELECT member_id, COUNT(*)
FROM orders GROUP BY member_id;
-- Extra: Using temporary (임시 테이블 사용)
`

Using index (좋음)



`sql
EXPLAIN SELECT member_id FROM orders WHERE member_id = 1;
-- Extra: Using index (커버링 인덱스!)
`

Using where (보통)



`sql
EXPLAIN SELECT * FROM orders WHERE total_amount > 10000;
-- Extra: Using where (WHERE 조건 필터링)
`

실전 최적화 예제



Before: 느린 쿼리



`sql
SELECT * FROM orders
WHERE status = 'PAID'
ORDER BY order_date DESC
LIMIT 10;

EXPLAIN:
- type: ALL
- key: NULL
- rows: 1,000,000
- Extra: Using filesort
-- 실행 시간: 5초
`

After: 최적화



`sql
-- 복합 인덱스 생성
CREATE INDEX idx_status_date ON orders(status, order_date DESC);

EXPLAIN:
- type: ref
- key: idx_status_date
- rows: 100
- Extra: Using index
-- 실행 시간: 0.01초 (500배 향상!)
`

N+1 문제 찾기



문제



`sql
-- 주문 100개 조회
SELECT * FROM orders LIMIT 100;

-- 각 주문마다 회원 정보 조회 (100번!)
SELECT * FROM members WHERE member_id = ?;
`

해결: JOIN 사용



`sql
SELECT o.*, m.*
FROM orders o
LEFT JOIN members m ON o.member_id = m.member_id
LIMIT 100;
-- 쿼리 1번으로 해결!
`

EXPLAIN ANALYZE (MySQL 8.0+)



실제 실행 시간까지 측정

`sql
EXPLAIN ANALYZE
SELECT * FROM orders WHERE member_id = 1;

-- 결과:
-- actual time=0.050..0.053 rows=10 loops=1
`

슬로우 쿼리 로그



활성화



`sql
-- my.cnf
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 -- 1초 이상 걸리는 쿼리 기록
`

분석



`bash
mysqldumpslow /var/log/mysql/slow.log

가장 느린 쿼리 Top 10


mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
`

최적화 체크리스트



1. WHERE 절



- [ ] 인덱스 있는 컬럼 사용
- [ ] 함수 사용 피하기 (
WHERE YEAR(date) ✗)
- [ ] LIKE는 앞부분 일치만 (
'홍%' ✓, '%홍%' ✗)

2. JOIN



- [ ] 조인 컬럼에 인덱스
- [ ] 불필요한 JOIN 제거
- [ ] LEFT JOIN보다 INNER JOIN (가능하면)

3. ORDER BY



- [ ] 인덱스 활용
- [ ] LIMIT과 함께 사용

4. SELECT



- [ ]
SELECT *` 피하기
- [ ] 필요한 컬럼만 조회

더 배우기



김영한의 실전 데이터베이스
- 쿼리 최적화 심화
- 실전 성능 튜닝
- 대용량 처리

---

**태그**: #EXPLAIN #MySQL최적화 #쿼리분석 #성능튜닝 #실행계획

L

럿지 AI 팀

AI 기술과 비즈니스 혁신을 선도하는 럿지 AI의 콘텐츠 팀입니다.