[MySQL] Query Plan

sunny·2025년 3월 8일

Query Plan이란?

  • MySQL에서는 조회 요청이 들어왔을 때, DBMS 내부 핵심엔진인 옵티마이저가 최적의 경로를 계산하여 조회를 수행하게 된다.
    • 이때 생성된 최적의 처리 경로를 실행계획(Query Plan)이라 한다.

왜/언제 사용할까?

나는 주로 아래와 같은 상황에서 사용한다.

  • 인덱스 생성 후 검수할 때 (인덱스가 잘 걸렸나!! 인덱스 잘 타나!!)
  • 조회 성능이 너무 떨어질 때 (쿼리 개선이 필요할 때)

사용법

  • 조회 쿼리 앞에 EXPLAIN 키워드만 붙이면 된다!
EXPLAIN SELECT * FROM recruitment r JOIN shelter s ON r.shelter_id = s.shelter_id ORDER BY r.created_at desc limit 0, 20;

키워드

  • id: 쿼리 내의 select문의 실행 순서 (id가 동일하면 같은 순서로 실행)
  • select_type: select문의 유형
  • table: 참조하고 있는 테이블명
  • partitions: 파티션 된 테이블을 사용했을 경우 그 파티션의 정보
  • type: 조인의 유형 (MySQL이 테이블을 액세스하는 방식)
    • 어떻게 조인되는 지를 나타내기 때문에 접근 방식이 효율적인지 판단 가능
    • 어떤 인덱스가 사용되고 이를 통해 어떻게 쿼리를 튜닝해야 하는지에 대한 인사이트 제공
  • possible_key: 테이블에서 row를 매핑하기 위해 사용 가능한 Key
  • key: 실제로 쿼리 실행에 사용된 Key로 possible_keys에 포함되지 않은 인덱스도 가능
  • key_len: 선택된 Index의 길이로 이 값이 너무 길다면 비효율적인 것 (Byte 단위이며 다중 인덱스도 가능)
  • ref: 행을 추출하는 데 Key와 함께 사용 된 컬럼이나 상수값
  • rows: 최초로 가져온 행의 개수로 통계 값이므로 실제 행과 반드시 일치하지 않음
  • filtered: WHERE 구의 검색 조건이 적용되면서 몇행이 남는지를 표시, 이 값도 통계 값이므로 반드시 일치하지 않음
  • extra: 옵티마이저가 동작하는 걸 알려주는 힌트

select_type

  • 쿼리의 유형 (즉, 쿼리가 어떤 방식으로 실행되는지)
  • 여러 개의 SELECT 문이 포함된 복잡한 쿼리(예: 서브쿼리, 조인 등)에서 MySQL이 각 부분을 어떻게 처리하는지 분석할 때 중요
select_type설명성능
SIMPLE서브쿼리나 UNION이 없는 단순한 SELECT✅ 빠름
PRIMARY가장 바깥쪽 SELECT (즉, 최상위 쿼리)✅ 빠름
SUBQUERYSELECT 문이 서브쿼리로 실행되는 경우⚠️ 성능 저하 가능
DEPENDENT SUBQUERY상위 쿼리의 컬럼을 참조하는 서브쿼리 (WHERE 안에 존재)❌ 느림
DERIVEDFROM 절에서 서브쿼리(SELECT FROM (SELECT ...))를 사용하는 경우 (파생 테이블)⚠️ 성능 저하 가능
UNCACHEABLE SUBQUERY상수 값이 아닌 변수를 기반으로 실행되어 결과를 캐싱할 수 없는 서브쿼리❌ 매우 느림
UNIONUNION을 사용한 쿼리에서 두 번째 이후의 SELECT✅ 괜찮음
UNION RESULTUNION의 최종 결과를 저장하는 임시 테이블⚠️ 임시 테이블 생성 가능
DEPENDENT UNIONUNION의 일부가 상위 쿼리에 의존하는 경우❌ 느림
  • 최적화 방법
    • DEPENDENT SUBQUERYJOIN으로 변경 → 성능 개선됨.
    • DERIVED 테이블이 크다면 TEMPORARY TABLE을 고려.
    • UNION 대신 UNION ALL 사용 → 불필요한 중복 제거를 피할 수 있음.

type

  • 조인의 유형(즉, MySQL이 테이블을 액세스하는 방식)
  • 성능이 좋은 순서대로 정리
type설명성능
system테이블이 한 행만 포함하고 있을 때 사용됨 (상수 테이블)🔥 가장 좋음
const기본 키나 유니크 인덱스를 이용하여 단 하나의 행만 조회할 때 사용됨🔥 매우 좋음
eq_ref조인 시 PRIMARY KEY 또는 UNIQUE KEY를 기준으로 매칭되는 경우
(조인 수행을 위해 각 테이블에서 하나의 행만 읽혀지는 형태)🔥 좋음
refeq_ref보다 덜 엄격한 조건, 보통 보조 인덱스를 이용한 조회👍 괜찮음
fulltextFULLTEXT 인덱스를 사용한 검색👍 괜찮음
ref_or_nullref와 유사하지만 NULL 값을 포함하는 경우😐 보통
index_merge여러 개의 인덱스를 병합하여 검색😐 보통
unique_subquery서브쿼리에서 IN (SELECT PRIMARY KEY FROM ...) 형태로 실행되는 경우😐 보통
index_subqueryunique_subquery와 유사하지만 PRIMARY KEY가 아닌 보조 인덱스를 사용😐 보통
range특정 범위 검색 (예: BETWEEN, >, <, IN(...) 등)😐 보통
index테이블의 모든 데이터를 인덱스를 통해 조회 (풀 인덱스 스캔)😢 느림
ALL테이블의 모든 데이터를 조회 (풀 테이블 스캔)❌ 매우 느림
  • 최적화 방법
    • ALL이나 index가 나오면 인덱스 추가 고려
    • range보다 ref가 더 효율적 → 가급적 인덱스 키를 조정하여 ref가 되도록 유도
    • eq_refconst가 나오도록 조인 최적화

0개의 댓글