Database/Mysql

[Real MySQL] 10-3. 실행 계획: 실행 계획 분석

noahkim_ 2025. 3. 12. 14:52

백은빈, 이성욱 님의 "Real MySQL" 책을 정리한 포스팅 입니다.

 

 

1. id 칼럼

  • 테이블들이 조인될 경우, 같은 id 값을 할당받음
  • id 값은 테이블의 접근 순서를 의미하지 않음
    • 같은 id라면 먼저 올라온 것이 먼저 접근된 순서임을 의미함

 

2. select_type 칼럼

  • 각 단위 select 쿼리가 어떤 타입의 쿼리인지 표시되는 컬럼

 

select_type 설명 예제
SIMPLE 단순한 SELECT 쿼리로, 서브쿼리를 사용하지 않음.  
PRIMARY UNION이나 서브쿼리를 포함하는 쿼리에서 가장 바깥쪽의 SELECT 쿼리.  
UNION UNION으로 결합된 SELECT 쿼리 중 두 번째 이후의 쿼리.  
DEPENDENT UNION 서브쿼리 내에서 UNION 또는 UNION ALL로 결합된 테이블.  
UNION RESULT UNION 결과를 담아내는 테이블.
UNION ALL에서는 사용되지 않음.
 
SUBQUERY 서브쿼리 내에서 사용되는 테이블.  
DEPENDENT SUBQUERY SELECT에서 사용된 서브쿼리에 참조되는 테이블.  
DERIVED FROM 절에 서브쿼리가 사용된 경우, 그 결과를 임시 테이블로 저장. SELECT * FROM (SELECT de.emp_no FROM dept_emp de GROUP BY de.emp_no) tb;
DEPENDENT DERIVED 서브쿼리에서 외부 칼럼을 참조하는 경우 SELECT * FROM employees e
  LEFT JOIN LATERAL (
     SELECT * FROM salaries s WHERE s.emp_no = e.emp_no 
  ) AS s2 ON s2.emp_no = e.emp_no
UNCACHEABLE SUBQUERY 서브쿼리의 결과가 캐시되지 않는 경우
결과가 매번 달라지는 서브쿼리에서 사용됨.
사용자 변수
NOT-DETERMINISTIC 속성의 (스토어드 루틴)
UUID()나 RAND()
UNCACHEABLE UNION UNION의 결과가 캐시되지 않는 경우.  
MATERIALIZED 서브쿼리를 임시 테이블로 구체화한 후, 해당 임시 테이블을 사용하여 최적화하는 기법.  

 

3. Table 컬럼

  • 실행 계획은 테이블 기준으로 표시됨 (쿼리 기준 X)
    • <>로 둘러쌓인 이름: 임시 테이블
    • <>로 둘러쌓인 숫자: select 쿼리의 id 값

 

4. partitions 컬럼

  • 파티션은 물리적으로 개별 테이블처럼 저장 공간을 가짐

 

파티션 프루닝
  • 파티션이 여러 개인 테이블에서 불필요한 파티션을 빼고 쿼리를 수행하기 위해 접근해야 할 것으로 판단되는 테이블만 골라내는 과정

 

5. type 컬럼

  • MySQL 서버가 각 테이블의 레코드를 어떤 방식으로 읽었는지를 나타냄

 

type 설명  
system 레코드가 한 건만 존재하거나, 한 건도 존재하지 않는 테이블을 참조하는 방식.
(MyISAM 또는 MEMORY 엔진에서만 사용)
 
const 프라이머리 키 또는 유니크 키의 모든 컬럼을 포함한 동등 조건이 있을 때 사용.
반드시 1건을 반환하는 쿼리.
인덱스의 일부 컬럼만 조건으로 사용할 때는 사용 불가.
SELECT * FROM users
WHERE user_id = 123;
eq_ref 첫 번째 테이블의 칼럼값을 이용해 두 번째 테이블의 프라이머리 키 또는 유니크 키로 동등 조건 검색
반드시 1건을 반환하는 쿼리.
조인에서 사용됨
SELECT o.order_id, c.customer_name
FROM orders o JOIN customers c 
ON o.customer_id = c.customer_id
WHERE o.order_id = 101;
ref 동등 조건으로 검색할 때 사용
반드시 1건만 반환될 필요는 없음.
조인에서 사용됨
SELECT o.order_id, c.customer_name
FROM orders o JOIN customers c 
ON o.customer_id = c.customer_id
WHERE c.city = 'Seoul';
fulltext 전문 검색 인덱스를 사용하여 레코드를 읽는 접근 방법.  
ref_or_null ref와 같으나, null 비교가 추가된 형태. SELECT o.order_id, c.customer_name
FROM orders o LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.city = 'Seoul' OR c.city IS NULL;
unique_subquery IN 연산자의 쿼리에서 서브쿼리 결과로 중복되지 않는 유니크한 값만 반환할 때 사용.  
index_subquery IN 연산자에서 중복된 값을 제거할 수 있을 때 사용. 서브쿼리 결과에서 중복 값을 인덱스를 이용해 제거.  
range 인덱스 레인지 스캔 방식의 접근 방법.  
index_merge 2개 이상의 인덱스를 이용해 각각의 검색 결과를 만들어낸 후, 그 결과를 병합하여 처리.
집합 연산 같은 부가적인 작업이 필요.
SELECT * FROM users
WHERE (first_name = 'John' AND last_name = 'Doe')
   OR (email = 'john.doe@example.com');
index 인덱스 풀 스캔을 의미.  
ALL 풀 테이블 스캔을 의미.  

 

6. possible_keys 컬럼

  • 실행 계획을 만들기 위해 후보로 선정했던 접근 방법에서 사용된 인덱스 목록

 

7. key 컬럼

  • 최종 선택된 실행 계획에서 사용하는 인덱스

 

8. key_len 컬럼

  • 처리를 위한 인덱스 사용 중, 각 레코드에서 몇 바이트까지 컬럼을 사용했는지 알려줌

 

9. ref 컬럼

  • 접근 방법이 ref 일때 사용됨
  • 참조 조건으로 어떤 값이 제공됐는지 보여줌

 

const
SELECT * FROM orders WHERE order_date = '2025-01-01';
  • 상숫값

 

테이블 명과 칼럼명
SELECT o.order_id, c.customer_name
FROM orders o JOIN customers c ON o.customer_id = c.customer_id
WHERE c.city = 'Seoul';
  • 다른 테이블의 컬럼값

 

func
SELECT * FROM orders WHERE YEAR(order_date) = 2025;
  • 컬럼값 가공

 

9. rows 컬럼

  • 쿼리를 처리하기 위해 얼마나 많은 레코드를 읽어야 하는지를 의미함
    • 인덱스를 사용하는 조건에만 일치하는 레코드 건수를 예측한 것
  • 스토리지 엔진별로 가지고 있는 통계 정보를 참조해서 만듬 (산출된 예상값이라 정확하지 않음)

 

10. filtered 컬럼

  • 인덱스 스캔이나 테이블 스캔에서 필터링된 레코드의 비율

 

SELECT * FROM users WHERE age >= 30;
  • users 테이블에 1000개의 레코드가 있음
  • age >= 30 을 만족하는 레코드가 200개라면, Filtered 컬럼 값은 200/1000 (0.2)가 됩니다.

 

11. Extra 컬럼

일반적인 실행 정보

메시지 설명
const row not found 조건을 만족하는 레코드가 없을 때 발생 (비용 타입의 테이블에서 조건을 만족하는 레코드 없음)
Deleting all rows 테이블의 모든 레코드를 삭제하는 경우
Distinct SELECT DISTINCT 구문으로 중복이 제거되는 경우
FirstMatch 조인을 최적화하여 첫 번째 일치하는 행을 찾으면 바로 종료하는 경우 (SEMI-JOIN 최적화)

 

조건 관련 메시지

메시지 설명
Full scan on NULL key
NULL 값을 포함하는 FULLTEXT 검색에서 발생
Impossible HAVING
HAVING 절에서 조건을 만족하는 레코드가 없을 때 발생
Impossible WHERE
WHERE 절에서 조건을 만족하는 레코드가 없을 때 발생
LooseScan
GROUP BY 최적화 기법인 LooseScan이 실행되는 경우

테이블 관련 메시지

메시지 설명
No matching min/max row
MIN() 또는 MAX()가 사용되었으나 일치하는 행이 없을 때 발생
No matching row in const table
const 타입으로 처리되는 테이블에 일치하는 행이 없을 때 발생
No matching row after partition pruning
파티션 테이블에서, 조건에 맞는 파티션이 없을 때 발생
No tables used
FROM 절 없이 실행되는 쿼리
Not exists
LEFT JOIN 최적화, 조인된 테이블에 일치하는 레코드가 없으면 즉시 종료

 

 

실행 계획 및 최적화 관련 메시지

메시지 설명
Plan isn't ready yet
실행 계획이 아직 생성되지 않았을 때 발생
Range checked for each record (index map: N)
동적 인덱스 레인지 스캔 최적화가 적용될 때 발생,
모든 행을 하나씩 검사하여 인덱스 범위를 결정
Recursive
WITH RECURSIVE 구문으로 재귀적으로 실행되는 경우
Rematerialize
Derived 테이블이 여러 번 실행될 때 필요할 때마다 다시 생성됨
Select tables optimized away
집계함수(GROUP BY, DISTINCT, COUNT()) 최적화가 적용된 경우, 테이블을 직접 조회하지 않음
Start temporary, End temporary
쿼리 실행 중에 임시 테이블이 사용된 경우

 

정렬 및 임시 테이블 관련 메시지

메시지 설명
Using filesort
ORDER BY를 위한 추가적인 정렬 연산이 필요한 경우
Using index
커버링 인덱스를 사용하여 테이블을 직접 조회하지 않고 인덱스에서 필요한 정보를 가져옴
Using index condition
Index Condition Pushdown 최적화가 적용된 경우
Using index for group-by
GROUP BY가 인덱스를 사용하여 최적화된 경우
Using index for skip scan
인덱스를 사용하여 스킵 스캔을 수행하는 경우
Using join buffer
Block Nested Loop, Batched Key Access, Hash Join 등 조인 최적화 기법이 사용된 경우
Using MRR
Multi-Range Read 최적화가 사용된 경우
Using sort_union
여러 개의 인덱스를 사용하여 결과를 병합하는 경우
Using temporary
임시 테이블을 사용하여 정렬, 집계 또는 중간 데이터 저장 수행
Using where
WHERE 조건이 사용된 경우
Zero limit
LIMIT 0이 사용된 경우