빙응의 공부 블로그

[Real MySQL 8.0]10장 - 실행계획 본문

CS/DB

[Real MySQL 8.0]10장 - 실행계획

빙응이 2024. 12. 23. 20:54

 

대부분의 DBMS는 많은 데이터를 안전하게 저장 및 관리하고 사용자가 원하는 데이터를 빠르게 조회하는 것이 목적이다.

이러한 목적 달성을 위해 옵티마이저가 사용자의 쿼리를 효율적으로 처리하는 실행 계획을 수립해야 한다. 하지만 옵티마이저가 관리자나 사용자의 개입 없이 항상 좋은 실행 계획을 만들어내는 것이 아니기에 EXPLAIN 명령어를 통해 옵티마이저가 수립한 실행 계획을 확인할 수 있게 해준다.

 

하지만 MySQL 서버에서 실행 계획을 이해하려면 MySQL 서버가 데이터를 처리하는 로직을 이해해야 한다

 

📝10.1 통계 정보

MySQL 8.0 버전부터 인덱스되지 않은 칼럼들에 대해 데이터 분포도를 수집하여 저장하는 히스토그램 정보가 도입되었다.

히스토그램이 도입됐다고 기존의 테이블이나 인덱스의 통계정보를 필요치 않은 것은 아니다. 정리하자면

  • MySQL 8.0 이전 : 인덱스에 대한 개괄적인 정보로 실행계획 수립
  • MySQL 8.0 이후 : 인덱스 뿐만 아니라 테이블에서 데이터 분포도를 수집하여 저장하는 히스토그램 정보도 도입

🧷10.1.1 테이블 및 인덱스 통계 정보 

비용 기반 최적화에서 가장 중요한 것은 통계 정보이다.

통계 정보가 정확하지 않다면 전혀 엉뚱한 방향으로 쿼리가 실행될 수 있다. 

예를들어 1억개의 데이터가 있지만 통계 정보가 갱신되지 않아 10건으로 보이면 인덱스 대신에 풀 테이블 스캔을 진행할 것이다.

 

📌10.1.1.1 MySQL 서버의 통계 정보 

5.5 버전까지는 각 테이블의 통계 정보가 메모리에만 관리되서 서버가 재시작되면 통계 정보가 모두 사라졌다. 5.6 버전부터는 각 테이블의 통계 정보를 mysql 데이터베이스의 innodb_index_stats 테이블과 innodb_table_stats 테이블로 관리할 수 있게 개선됐다.

STATS_PERSISTENT 옵션으로 영구적으로 저장할지 말지를 결정할 수 있다. (기본값은 1(on))

STATS_AUTO_RECALC 옵션으로 통계 정보를 자동으로 수집할지 여부를 결정할 수 있다.

 

🧷10.1.2 히스토그램

MySQL 5.7 버전까지의 통계 정보는 단순히 인덱스된 칼럼의 유니크한 값의 개수 정도만 가지고 있었습니다.

이는 옵티마이저가 실행 계획을 수립하기에는 많이 부족했습니다. 그래서 옵티머이저는 이러한 부족함을 메우기 위해 실제 인덱스의 일부 페이지를 랜덤으로 가져와 참조하는 방식을 사용했는데, 8.0 버전부터는 MySQL 서버도 칼럼의 데이터 분포도를 참조하는 히스토그램을 사용합니다. 

 

📌10.1.2.1 히스토그램 정보 수집 및 삭제

MySQL 8.0 버전에서 히스토그램 정보는 칼럼 단위로 관리됩니다. 이는 자동으로 수집되지 않고 수동으로 수집, 관리됩니다.

 

수집된 히스토그램 정보는 시스템 딕셔너리에 함께 저장되고, MySQL 서버가 시작될 때 딕셔너리의 히스토그램 정보를 information_schema 데이터베이스의 column_statistics 테이블로 로드합니다. 그래서 실제 조회는 column_statistics 테이블를 SELECT해서 참조할 수 있습니다.

 

히스토그램은 2가지 타입을 지원합니다.

  • Singleton(싱글톤 히스토그램): 칼럼 값 개별로 레코드 건수를 관리하는 히스토그램입니다. Value-Based 히스토그램 또는 도수 분포라고 불립니다.
  • Equi-Height(높이 균형 히스토그램): 칼럼 값의 범위를 균등한 개수로 구분해서 관리하는 히스토그램입니다.

히스토그램은 버킷 단위로 구분되어 레코드 건수나 칼럼값의 범위가 관리됩니다. 

싱글톤 히스토그램은 칼럼이 가지는 값별로 버킷이 할당되고 높이 균형 히스토그램은 개수가 균등한 칼럼의 범위별로 하나의 버킷을 할당합니다. 

 

📌10.1.2.2 히스토그램의 용도 

MySQL 서버에서는 히스토그램 이전에도 테이블과 인덱스에 대한 통계 정보는 존재했습니다.

하지만 기존 MySQL 서버의 통계 정보는 테이블의 전체 레코드 건수와 인덱스된 칼럼이 가지는 유니크한 값의 개수 정도였습니다.

하지만 이 통계 정보는 항상 균등한 분포도를 가지지않아 히스토그램이 도입된 것입니다.

 

예를 들어봅시다. 레코드가 1000개 유니크한 값이 100개 였다면 히스토그램 이전에는 동등 비교 검색이 대략 10개를 나올 것이라 예측합니다. 하지만 유니크 값이 레코드에 균등한 분포도를 가지지 않았습니다.

 

이는 쿼리 조회에 대해 상당히 유의미한 성능 차이를 낳습니다. 

히스토그램이 존재하면  실제 특정 범위의 데이터가 많고 적음을 판단할 수 있기에 쿼리 성능이 확실히 개선되었습니다..

📌10.1.2.3 히스토그램과 인덱스

히스토그램과 인덱스는 공통점이 있습니다. 인덱스도 부족한 통계 정보를 수집하기 위해 사용됩니다. 

MySQL 서버에서는 쿼리의 실행 계획을 수립할 때 사용 가능한 인덱스로부터 조건절에 일치하는 레코드 건수를 대략적으로 파악하고 실행 계획을 세웁니다.

이때 조건절에 일치하는 레코드 건수를 예측하기 위해 옵티마이저는 실제 인덱스의 B-Tree를 샘플링 해봅니다. 

 

이것을 인덱스 다이브라고 합니다.

인덱스 다이브의 특징은 인덱스가 존재하는 칼럼에 대해 검색을 하는 경우 히스토그램을 사용하지 않고 인덱스 다이브를 한다는 것입니다. 이것은 실제 인덱스를 샘플링한 정보가 항상 히스토그램보다 정확하기 때문입니다.

 

🧷10.1.3 코스트 모델 

MySQL 서버가 쿼리를 처리하려면 다음과 같은 다양한 작업을 필요로 합니다.

  • 디스크로부터 데이터 페이지 읽기
  • 메모리로부터 데이터 페이지 읽기
  • 인덱스 키 비교
  • 레코드 평가
  • 메모리 임시 테이블 작업
  • 디스크 임시 테이블 작업

MySQL 서버는 사용자의 쿼리에 대해 이러한 다양한 작업이 얼마나 필요한지 예측하고 전체 작업 비용을 계산한 결과를 바탕으로 최적의 실행 계획을 찾습니다.

이러한 전체 쿼리의 비용을 계산하는 데 필요한 단위 작업들의 비용을 코스트 모델이라고 합니다.

 

📝10.2 실행 계획 확인

MySQL 서버의 실행 계획은 DESC 또는 EXPLAIN 명령으로 확인이 가능합니다. 

🧷10.2.1 실행 계획 출력 포맷

이전 버전에는 EXPLAIN 명령 시 명령을 구분해서 포맷을 설정했지만 이젠 모든 내용이 통합되면서 따로 포맷이 가능해졌습니다.

그래서 MySQL 8.0버전부터 포맷을 정할 수 있게 되었습니다.

테이블 포맷
EXPLAIN
SELECT *
FROM employees
WHERE first_name='ABC';
트리 포맷
EXPLAIN FORMAT=TREE
SELECT *
FROM employees
WHERE first_name='ABC';
JSON 포맷
EXPLAIN FORMAT=JSON
SELECT *
FROM employees
WHERE first_name='ABC';

🧷10.2.2 쿼리 실행 시간 확인

MySQL 8.0.18 버전부터는 쿼리의 실행 계획과 단계별 소요된 시간 정보를 확인할 수 있는 EXPLAIN ANALYZE 기능이 추가 되었습니다. 

EXPLAIN ANALYZE
SELECT id
FROM employees
WHERE name='Matt'
-> Filter: (employees.`name` = 'Matt')  (cost=1.5 rows=1) (actual time=7.92..7.92 rows=0 loops=1)
     -> Table scan on employees  (cost=1.5 rows=5) (actual time=7.9..7.91 rows=5 loops=1)

 

해당 기능은 항상 결과를 TREE 포맷으로 보여주기에 호출 순서에 따른 시간을 볼 수 있습니다.

여기서 들여쓰기는 호출 순서를 의미하며 다음과 같이 실행됩니다.

  1. employees 테이블 풀 스캔
  2. name = Matt인 필터 적용

EXPLAIN ANALYZE 명령은 EXPLAIN 명령과 달리 실행 계획만 추출하는 것이 아니라 실제 쿼리를 실행하고 사용된 실행 계획과 소요된 시간을 보여줍니다. 그래서 쿼리 시간이 매우 오래걸리면 해당 작업이 끝나고 결과를 확인 가능하다.

 

📝10.3 실행 계획 분석

실행 계획이 어떤 접근 방법을 사용해서 어떤 최적화를 수행하는지, 그리고 어떤 인덱스를 사용하는지 등을 이해하는 것은 매우 중요하다. 기존 테이블 포맷으로 출력되던 실행 계획을 이해하면 포맷이 바뀌어도 어렵지 않게 실행 계획을 이해할 수 있다. 

 

기본적인 실행 계획 명령을 실행하면 쿼리 문장의 특성에 따라 표 형태로 된 결과가 표시된다.

이 표의 각 라인은 쿼리 문장에서 사용된 테이블의 개수만큼 출력된다. 실행 순서는 위에서 아래로 순서대로 표시된다. 

그래서 첫번째 라인은 드라이빙 테이블인것이다.

 

이제 테이블 칼럼의 의미를 알아보자.

 

🧷10.3.1 id 칼럼

하나의 SELECT 문장은 다시 1개 이상의 하위 SELECT 문장을 포함할 수 있다. 다음 쿼리를 살펴보자

SELECT ...
FROM (SELECT ... FROM tb_Test1) tb1, tb_test2 tb2
WHERE tb1.id=tb2.id

 

위의 쿼리 문장에 있는 각 SELECT는 다음과 같이 분리해서 생각볼 수 있다. 이렇게 SELECT 키워드 단위로 분리한 것을 단위 쿼리라고 한다.

 SELECT ... FROM tb_test1;
 SELECT ... FROM tb1, tb_test tb2 WHERE tb1.id=tb2.id;

 

실행 계획에서 가장 왼쪽에 표시되는 id 칼럼은 단위 SELECT 쿼리 별로 부여되는 식별자 값입니다.

이 예제 쿼리의 경우 실행 꼐획에서 최소 2개의 id 값이 표시될 것입니다. 하나의 SELECT 문장 안에서 여러개의 테이블을 조인하면 조인되는 테이블의 개수만큼 실행 계획 레코드가 출력되지만 id 값은 동일하게 부여됩니다.

 

그래서 조인이 아닌 아래와 같이 단위 쿼리를 구성하면 모두 다른 id가 할당됩니다.

해당 예제는 3개의 SELECT를 사용했기에 3개의 ID 값이 생성됩니다.

EXPLAIN
SELECT
((SELECT COUNT(*) FROM employees) + (SELECT COUNT(*) FROM departments) AS total_count;

🧷10.3.2 select_type 칼럼

각 단위 SELECT 쿼리가 어떤 타입의 쿼리인지 표시되는 칼럼입니다.

 

1. SIMPLE

  • 설명: SIMPLE은 UNION이나 서브쿼리 없이 단일 SELECT 쿼리를 실행할 때 사용됩니다.
  • 특징: 쿼리 문장이 아무리 복잡하더라도 단위 쿼리가 하나만 존재하면 SIMPLE로 표시됩니다.
  • 예시:
SELECT name FROM employees WHERE age > 30;

 

2. PRIMARY

  • 설명: PRIMARY는 UNION이나 서브쿼리를 포함하는 쿼리에서 가장 바깥쪽 SELECT 문을 나타냅니다.
  • 특징: PRIMARY는 단위 쿼리가 하나만 존재하며, 외부 쿼리에서 최종 결과를 반환합니다.
  • 예시:
SELECT * FROM (SELECT * FROM employees WHERE age > 30) AS subquery;

 

3. UNION

  • 설명: UNION은 두 개 이상의 SELECT 쿼리 결과를 결합할 때 사용됩니다. 첫 번째 SELECT는 DERIVED로 표시되며, 그 이후의 SELECT는 UNION으로 표시됩니다.
  • 특징: UNION으로 결합된 쿼리에서 첫 번째 쿼리는 임시 테이블로 처리되고, 나머지 쿼리는 UNION 타입으로 표시됩니다.
  • 예시:
SELECT id FROM employees WHERE age > 30 UNION SELECT id FROM employees WHERE department = 'Sales';

 

4. DEPENDENT UNION

  • 설명: DEPENDENT UNION은 UNION ALL로 집합을 결합할 때 표시됩니다. 이 타입은 내부 쿼리가 외부 쿼리의 값을 참조해서 처리될 때 사용됩니다.
  • 특징: 내부 쿼리가 외부 쿼리의 컬럼을 참조하거나 외부 값에 의존적인 경우, DEPENDENT UNION으로 표시됩니다.
  • 예시:
SELECT id FROM employees WHERE age > (SELECT MAX(age) FROM employees WHERE department = 'Sales')
UNION ALL SELECT id FROM employees WHERE department = 'Engineering';

 

5. UNION RESULT

  • 설명: UNION RESULT는 UNION 결과를 저장하는 임시 테이블입니다.
  • 특징: UNION의 최종 결과를 나타내며, 별도의 단위 쿼리가 아니기 때문에 id 값이 NULL로 표시됩니다.
  • 예시: UNION으로 결합된 두 쿼리의 결과를 표시할 때 이 타입이 사용됩니다.

6. SUBQUERY

  • 설명: SUBQUERY는 FROM 절을 제외한 모든 서브쿼리를 표시합니다. FROM 절에 사용된 서브쿼리는 DERIVED로 표시됩니다.
  • 특징: SUBQUERY는 임시 테이블을 만들지 않고, 서브쿼리의 결과를 외부 쿼리에서 사용하는 방식입니다.
  • 예시:
SELECT id FROM employees WHERE age > (SELECT AVG(age) FROM employees);

 

7. DEPENDENT SUBQUERY

  • 설명: DEPENDENT SUBQUERY는 서브쿼리가 PRIMARY 타입의 SELECT에서 사용될 때 표시됩니다.
  • 특징: 가장 느리게 처리되는 쿼리로, 외부 쿼리와의 의존 관계로 인해 성능에 영향을 미칠 수 있습니다.
  • 예시:
SELECT name FROM employees WHERE salary > (SELECT MAX(salary) FROM employees WHERE department = 'HR');

 

8. DERIVED

  • 설명: DERIVED는 서브쿼리의 결과가 임시 테이블로 저장되고 외부 쿼리에서 이 임시 테이블을 사용하게 될 때 사용됩니다.
  • 특징: 서브쿼리에서 한 번에 실행되며, 결과는 임시 테이블로 저장되어 외부 쿼리에서 사용됩니다.
  • 예시:
SELECT d.name, e.name FROM departments d 
JOIN (SELECT id, name FROM employees WHERE salary > 5000) e ON d.id = e.department_id;

 

9. DEPENDENT DERIVED

  • 설명: DEPENDENT DERIVED는 래터럴 조인 기능에서 사용됩니다.
  • 특징: FROM 절의 서브쿼리에서 외부 칼럼을 참조할 때 주로 사용됩니다.
  • 예시:
SELECT e.name, d.department_name, salary_info.max_salary
FROM employees e
JOIN LATERAL (
    SELECT MAX(salary) AS max_salary
    FROM employees
    WHERE department_id = e.department_id
) salary_info
ON TRUE;

 

10. UNCACHEABLE SUBQUERY

  • 설명: UNCACHEABLE SUBQUERY는 서브쿼리의 결과를 캐시할 수 없을 때 발생합니다. 서브쿼리 결과가 캐시되지 않으며, 여러 번 실행됩니다.
  • 특징:
    • 서브쿼리의 결과를 재사용할 수 없고, 매번 새로 실행됩니다.
    • 서브쿼리가 변동 가능한 외부 값을 참조하거나, 복잡한 계산을 포함할 때 캐시가 불가능합니다.
  • 서브 쿼리를 캐시할 수 없는 경우
    • 사용자 변수를 사용한 서브 쿼리
    • NOT_DETERMINISTIC 속성의 스토어드 루틴이 서브쿼리 내 사용
    • UUID나 랜덤같은 결과값이 호출마다 달라지는 것 

 

11. UNCACHEABLE UNION

  • 설명: UNCACHEABLE UNION은 UNION 연산을 사용할 때, 서브쿼리 결과가 캐시될 수 없을 경우 발생합니다. UNION이나 UNION ALL과 함께 사용할 때, 서브쿼리 결과를 여러 번 계산해야 하기 때문에 캐시할 수 없습니다.
  • 특징:
    • UNION 또는 UNION ALL이 포함된 서브쿼리에서 결과가 캐시되지 않습니다.
    • UNION에 의해 반환된 결과가 동적으로 변경될 때 캐시할 수 없습니다.

 

12. MATERIALIZED

  • 설명: MATERIALIZED는 주로 FROM 절이나 IN 형태의 쿼리에 사용되며 서브쿼리를 최적화한다.
  • 특징:
    • 서브쿼리의 결과를 임시테이블로 구체화합니다. 
    • 서브쿼리 결과를 저장한 후 테이블과 조인하여 최적화합니다.

🧷10.3.3 table 칼럼

실행 계획은 테이블 기준으로 표시됩니다.. table 컬럼에는 실행 계획의 테이블 이름이 들어갑니다.. , <union M,N> 과 같이 “<>”로 둘러싸인 경우는 임시 테이블을 의미하고, 안의 숫자는 id값을 지칭합니다.

🧷10.3.4 partitions 칼럼

불필요한 파티션을 빼고 쿼리를 수행하기 위해 접근해야 할 테이블만 골라내는 과정을 파티션 프루닝이라 합니다. 파티션 컬럼은 해당 쿼리가 어느 파티션에 접근했다는 것을 알려줍니다..

 

🧷10.3.5 type 칼럼

쿼리에서 사용된 조인 방식이나 테이블 접근 방법을 나타냅니다.

 

  1. ALL:
    • 설명: 전체 테이블을 스캔하는 방식입니다. 인덱스를 사용하지 않고, 테이블의 모든 행을 검색하여 조건에 맞는 데이터를 찾습니다.
    • 특징: 대량의 데이터가 있는 테이블에서는 성능이 좋지 않을 수 있습니다. 이 방식은 주로 작은 테이블이나 인덱스가 없는 테이블에 사용됩니다.
  2. index:
    • 설명: 인덱스를 사용하여 테이블을 스캔합니다. ALL과 비슷하지만, 인덱스를 이용해 빠르게 검색합니다.
    • 특징: 테이블을 스캔하지만, 인덱스를 활용하여 데이터 검색 속도를 향상시킵니다. 일반적으로 index는 ALL보다 효율적입니다.
  3. range:
    • 설명: 범위 인덱스를 사용하여 테이블을 스캔하는 방식입니다. WHERE 절에서 범위 조건(예: BETWEEN, >, <)을 사용하는 경우에 사용됩니다.
    • 특징: 범위 검색을 통해 조건에 맞는 데이터만 선택하므로, 성능이 향상됩니다. 주로 범위가 제한된 쿼리에서 사용됩니다.
  4. ref:
    • 설명: 다른 테이블과의 비교를 위해 인덱스를 사용한 검색 방식입니다. 주로 JOIN에서 외부 테이블의 값을 기준으로 검색할 때 사용됩니다.
    • 특징: ref는 range보다 효율적인 방식입니다. 외부 테이블의 인덱스를 사용하여 해당 테이블의 값을 찾고 비교합니다.
  5. eq_ref:
    • 설명: 정확한 일치를 찾기 위한 조인 방식입니다. 주로 PRIMARY KEY나 UNIQUE 키로 조인하는 경우 사용됩니다.
    • 특징: 일치하는 값이 하나만 반환되므로 매우 효율적입니다. JOIN에서 두 테이블 간의 일대일 관계를 처리할 때 사용됩니다.
  6. const:
    • 설명: PRIMARY KEY 또는 UNIQUE 키 칼럼을 이용한 조건 검색으로, 반드시 1건만 반환하는 처리 방식입니다.
    • 특징: 조건에 맞는 값이 하나만 존재함을 보장하며, 이 경우 쿼리는 매우 빠릅니다. 보통 WHERE 절에서 고유 식별자 값을 기준으로 검색할 때 사용됩니다.
  7. system:
    • 설명: 시스템 테이블을 접근하는 방식입니다. MySQL의 시스템 테이블을 참조하는 경우 사용됩니다.
    • 특징: 시스템 테이블은 보통 매우 작은 테이블이므로 system은 빠른 검색 속도를 제공합니다.
  8. index_merge:
    • 설명: 여러 인덱스를 병합하여 데이터를 검색하는 방식입니다. 여러 인덱스를 조합하여 쿼리 조건에 맞는 데이터를 찾습니다.
    • 특징: 복잡한 쿼리에서 여러 조건을 만족하는 인덱스를 조합하여 최적화하는 방법입니다. 성능 향상에 도움이 되지만, 복잡한 쿼리에서는 비효율적일 수 있습니다.

🧷10.3.6 possible_keys 칼럼

쿼리에서 사용 가능한 인덱스를 나열합니다. 이 컬럼은 쿼리 실행 계획에서 인덱스를 사용 가능함을 나타내며, 실제로 사용될 인덱스는 key 컬럼에서 확인할 수 있습니다.

🧷10.3.7 key 칼럼

실행 계획에서 실제로 사용된 인덱스를 나타냅니다. possible_keys에서 나열된 인덱스 중 사용된 인덱스를 이 칼럼에서 확인할 수 있습니다.

🧷10.3.8 key_len 칼럼

인덱스가 몇 개의 컬럼을 사용하는지에 대한 정보를 제공합니다. 예를 들어, key_len이 5라면 5개의 컬럼을 포함하는 인덱스가 사용된 것입니다.

🧷10.3.9 ref 칼럼

조인 조건에서 다른 테이블이나 상수와 비교되는 컬럼을 나타냅니다. 예를 들어, const는 상수 값을 의미하고, field는 특정 컬럼을 의미합니다.

🧷10.3.10 row 칼럼

해당 단계에서 MySQL이 예상하는 처리해야 할 행의 수입니다. 이 값은 MySQL이 쿼리 실행을 최적화하기 위해 계산한 추정값입니다.

🧷10.3.11 filtered 칼럼

 fintered 컬럼은 인덱스를 사용한 조건으로 걸러진 레코드들 중 인덱스를 사용하지 못하는 조건으로 인해 필터링되고 남은 레코드의 비율을 의미합니다. 즉, rows가 233이고, filtered가 16.03이면 결과 레코드 건수는 233 * 0.1603 = 37이 됩니다.

🧷10.3.12 Extra 칼럼

쿼리 실행 계획에서 성능과 관련된 중요한 내용이 표시된다.

주로 내부적인 처리 알고리즘에 대해 조금 더 깊이 있는 내용을 보여준다.

1. Distinct

  • SELECT DISTINCT를 사용한 경우 중복 제거를 위해 별도의 최적화가 이루어집니다. 테이블에서 일부 레코드만 조인하여 처리합니다.

2. FirstMatch, LooseScan

  • 세미 조인 전략에서 FirstMatch 또는 LooseScan 전략이 사용된 경우 표시됩니다.

3. Full scan on NULL key

  • col1 IN (SELECT col2 FROM …)와 같은 조건에서, col1이 NULL인 경우 서브쿼리 테이블에 대해 풀 스캔이 필요함을 나타냅니다.

4. Impossible HAVING, WHERE

  • HAVING 또는 WHERE 절의 조건이 항상 FALSE일 때 표시됩니다.

5. No matching min/max row

  • MIN이나 MAX 함수에서 대상이 NULL일 때 표시됩니다.

6. no matching row in const table

  • const 접근 방식으로 테이블을 읽었으나 일치하는 레코드가 없을 때 표시됩니다.

7. No matching rows after partition pruning

  • 파티션된 테이블에서 UPDATE나 DELETE 명령을 실행할 때, 해당 파티션이 없을 경우 표시됩니다.

8. Not exists

  • NOT IN 또는 NOT EXISTS 연산자로 수행되는 안티 조인에서 표시됩니다. 예를 들어, LEFT JOIN과 WHERE IS NULL 조건을 사용한 쿼리에서 나타납니다.

9. Plan isn’t ready yet

  • 쿼리 실행 계획이 아직 수립되지 않았을 때 표시됩니다.

10. Range checked for each record (index map: N)

  • 레코드마다 인덱스 범위를 체크해야 할 때 표시됩니다. index map: N은 사용할 인덱스 후보들에 대한 정보를 제공합니다.

11. Recursive

  • CTE(공통 테이블 표현식)를 사용한 재귀 쿼리에서 표시됩니다.

12. Rematerialize

  • 래터럴 조인 시 서브쿼리 결과를 임시 테이블에 저장하는 과정에서 표시됩니다.

13. Select tables optimized away

  • MIN() 또는 MAX()와 같은 집계 함수만 사용할 때 인덱스 최적화가 적용되어 표시됩니다.

14. Start temporary, End temporary

  • 세미 조인 최적화에서 중복 제거를 위해 임시 테이블을 사용하는 경우 표시됩니다.

15. Unique row not found

  • 유니크 컬럼을 기준으로 아우터 조인을 수행하는 쿼리에서, 일치하는 레코드가 없을 때 표시됩니다.

16. Using filesort

  • ORDER BY에 인덱스를 사용하지 못할 때 표시됩니다. 이는 성능 저하를 초래할 수 있습니다.

17. Using index (커버링 인덱스)

  • 인덱스만 읽어서 쿼리를 처리할 수 있는 경우 표시됩니다. 디스크 접근이 필요 없으므로 성능 향상 효과가 있습니다.

18. Using index condition

  • 인덱스 조건 푸시 다운 최적화를 사용할 때 표시됩니다.

19. Using index for group-by

  • 인덱스를 사용하여 GROUP BY를 처리하는 경우 표시됩니다. 별도의 정렬 작업 없이 인덱스의 필요한 부분만 읽습니다.

20. Using index for skip scan

  • 인덱스 스킵 스캔 최적화를 사용할 때 표시됩니다.

21. Using join buffer (hash join, Batched Key Access)

  • 조인 버퍼를 사용하는 경우 표시됩니다.

22. Using MRR

  • MRR(Multi-Range Read) 최적화를 사용할 경우 표시됩니다.

23. Using union, sort_union, intersect

  • index_merge 방식으로 UNION이나 INTERSECT가 실행될 때 해당 방식이 표시됩니다.

24. Using temporary

  • 쿼리 실행 중 임시 테이블이 생성될 때 표시됩니다.

25. Using where

  • MySQL 엔진에서 WHERE 절의 필터링 작업을 처리한 경우 표시됩니다.