💡 개요
DB 성능을 향상시키기 위해 쿼리 플랜을 살펴본다는데, 이게 뭘까?
🛠️ 쿼리 플랜
DB는 우리가 쿼리(SQL)을 보내면, 그 쿼리를 실행하기 위한 '실행 계획'을 세운다.
그 계획을 우리는 ‘실행 계획’이나 ‘쿼리 플랜’이라고 표현한다.
쿼리 플랜을 조회하기 위해선 다음과 같은 SQL문을 실행하면 된다.
EXPLAIN
SELECT * FROM restrooms WHERE address LIKE '%송정동%';
위 쿼리는 주소에 ‘송정동’이 포함된 화장실 정보를 조회하는 쿼리이다.
쿼리 플랜을 조회하면 다음과 같은 결과가 나온다.
근데 보다시피 무슨 컬럼이 많다.
일단 쿼리 플랜을 조회할 경우 알아야할 컬럼을 정리하자.
⚙️ 쿼리 플랜 컬럼
컬럼명 | 설명 |
id | 쿼리 실행 순서를 나타냄. 쿼리가 단순하면 보통 1만 나옴. 서브쿼리나 UNION이 있으면 여러 개 생김. |
select_type | SELECT 문 종류. 예: SIMPLE(기본), PRIMARY(메인쿼리), SUBQUERY(서브쿼리), DERIVED(인라인 뷰) 등. |
table | 해당 row가 어떤 테이블의 실행 계획인지 나타냄. |
partitions | 파티션이 걸린 테이블이라면, 어떤 파티션이 조회되는지 표시함. (파티셔닝 안 하면 생략됨) |
type | 접근 방식(조사 범위). ALL이면 풀스캔, index, range, ref, eq_ref, const 등으로 갈수록 성능 좋음. |
possible_keys | MySQL이 사용할 수 있을 것 같은 인덱스 후보 리스트. 실제 사용한 건 아님. |
key | 실제 사용한 인덱스 이름. NULL이면 인덱스를 안 썼다는 뜻. |
key_len | 사용된 인덱스의 길이(byte). 어느 정도 인덱스를 활용했는지 알 수 있음. |
ref | 조인 조건으로 어떤 컬럼/상수가 사용됐는지 보여줌. |
rows | MySQL이 예측하는 읽게 될 row 수. 클수록 성능 부담이 큼. |
filtered | 조건을 만족하는 row의 비율(%) 예측값. rows * (filtered/100)이 실제 반환 예상 row 수. |
Extra | 기타 정보. Using where, Using index, Using temporary, Using filesort 등 성능 관련 힌트가 여기에 있음. |
고마워요 ChatGPT.
그런데 알아야할 컬럼이 좀 많군요..
정리해보면 아래 4개 컬럼을 위주로 알고 있으면 좋을 듯 하다.
- type : ALL이면 느린 쿼리. range, ref 이상이면 인덱스 사용 가능성 있음.
- key : 실제로 사용된 인덱스. NULL이면 인덱스 사용 안 함.
- rows : 예상 조회 수. 작을수록 좋음.
- Extra : Using filesort, Using temporary는 성능에 안 좋은 힌트.
이제 다시 위의 쿼리 플랜으로 돌아가 보자.
먼저 눈에 띄는 컬럼은 type이다.
해당 쿼리에서는 type = ALL이 나왔는데, 이는 우리가 정리한 컬럼 설명 중에서도 가장 느린 실행 방식, 즉 풀 테이블 스캔을 의미한다.
그럴 만도 한 게, 우리가 실행한 쿼리는 인덱스를 활용하지 않고, address 컬럼의 문자열 포함 여부(LIKE '%송정동%')만으로 필터링했기 때문이다.
이 경우 인덱스를 쓸 수 없어서 DB는 모든 행을 하나하나 확인할 수밖에 없다.
그렇다면 인덱스가 적용된 컬럼을 사용하면 쿼리 플랜은 어떻게 달라질까?
예를 들어, 기본 키인 id 컬럼을 사용한 아래의 쿼리를 보자.
EXPLAIN
SELECT * FROM restrooms WHERE id < 100;
뭐가 좀 달라졌다.
이전까진 type이 ALL이었지만 이번엔 range가 되었다.
아마 id < 100 와 같이 id의 범위를 토대로 데이터를 조회해서 그런 것 같다.
→ 즉, ALL보다는 훨씬 효율적인 방식이다!
🤔 결론
왜 개발자 오픈채팅방이나 기술 블로그에서 "성능 저하가 발생했다면 쿼리 플랜을 확인하라"는 이야기가 나오는지 이제 이해가 된다.
자주 호출되는 쿼리일수록, 그 쿼리의 실행 계획을 미리 확인하고, type = ALL처럼 성능을 저해할 수 있는 요소가 있다면 반드시 개선이 필요하다.
단순히 쿼리를 잘 작성하는 것뿐 아니라, DB가 어떻게 실행할지까지 고민하는 습관이 개발자에겐 정말 중요하다는 걸 느꼈다.
'개발 일기' 카테고리의 다른 글
[개발 일기] 2025.06.27 - Stateful과 Stateless의 차이 (0) | 2025.06.27 |
---|---|
[개발 일기] 2025.06.26 - AOT (0) | 2025.06.26 |
[개발 일기] 2025.06.24 - 난 분명히 토큰을 넣었다고 했다. (1) | 2025.06.24 |
[개발 일기] 2025.06.23 - usingRecursiveComparison() (0) | 2025.06.23 |
[개발 일기] 2025.06.22 - 의존의 흐름? (0) | 2025.06.22 |