Skip to the content.

Explain

https://dev.mysql.com/doc/refman/8.0/en/explain.html

기본 형식

EXPLAIN query;
/* Example
id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1	SIMPLE	b	p1,p2,p3	ref	b_pk,b_ix1	b_pk	4	const	8	100.00	Using where; Using temporary; Using filesort
1	SIMPLE	a		ref	a_pk	a_pk	4	100.00	const	216720	Using where
*/


내용 설명

|Type|설명| |-|-| |id|SELECT를 구분하는 번호| |select_type|SELECT 타입 구분| |table|참조하는 테이블| |partitions|참조하는 파티션| |type|조인, 조회 타입| |possible_keys|사용 가능한 인덱스 목록 |key|옵티마이저가 실제로 사용할 인덱스 |key_len|실제로 사용할 인덱스의 길이 |ref|인덱스에서 값을 찾기 위해 비교할 컬럼 (상수) |rows|쿼리 실행 시 접근할 행 (예상 값이므로 일치하지 않을 수 있음) |filtered|쿼리 실행 시 필터링된 비율 (rows에서 100이고 filtered에서 20%라면 필터링된 레코드 수가 20이라는 의미) |extra|옵티마이저 동작 정보


select_type

|이름|설명| |-|-| |SIMPLE|UNION, 서브 쿼리가 없는 단순 SELECT| |PRIMARY|UNION, 서브 쿼리인 경우의 첫번째 쿼리| |UNION|UNION 쿼리에서 PRIMARY를 제외한 나머지| |DEPENDENT_UNION|UNION과 동일하지만 외부 쿼리의 영향을 받음| |UNION_RESULT|UNION 쿼리의 결과| |SUBQUERY|서브 쿼리나 서브 쿼리를 구성하는 여러 쿼리 중 첫번쨰 쿼리| |DEPENDENT_SUBQUERY|서브 쿼리와 동일하지만 외부 쿼리의 영향을 받음| |DERIVED|SELECT로 추출된 테이블 (FROM 에서의 서브 쿼리 또는 인라인 뷰)| |UNCACHEABLE SUBQUERY|서브 쿼리와 동일하지만 외부 쿼리의 모든 값에 서브 쿼리를 다시 처리| |UNCACHEABLE UNION|UNION과 동일하지만 외부 쿼리의 모든 값에 UNION 쿼리를 다시 처리|


type

|이름|설명| |-|-| |system|테이블에 데이터가 1개 밖에 없음| |const|Primary Key나 Unique Key로 조인이 아닌 가장 외부 테이블에 접근함 (결과는 1개)| |eq_ref|Primary Key로 조인 (조인의 내부 테이블에 접근)| |ref|인덱스로 조인 (여러 행에 접근 가능)| |ref_or_null|ref와 동일하지만 NULL도 같이 포함하여 검색| |index_merge|여러 개의 인덱스를 사용| |unique_subquery|IN 안에 있는 서브 쿼리에서 Primary Key나 Unique Key를 사용| |index_subquery|unique_subquery와 비슷하나 Primary Key나 Unique Key가 아닌 인덱스를 사용| |range|인덱스를 특정 범위 내에서 사용| |index|인덱스 풀스캔| |all|테이블 풀스캔|


extra

|이름|설명| |-|-| |Using where|행을 가져온 후 추가적으로 검색조건을 적용해 행의 범위를 축소 (타입이 all, index라면 좋지 않음)| |Using index|테이블에는 접근하지 않고 인덱스로만 접근 (커버링 인덱스)| |Using index for group-by|Using index와 비슷하지만 GROUP BY가 포함되어 있는 쿼리를 커버링 인덱스로 접근이 가능| |Using filesort|데이터 정렬이 필요 (filesort 사용 (MySQL의 quick sort), 데이터가 많다면 성능에 영향)| |Using temporary|내부적으로 임시 테이블이 생성| |Using where with pushed|엔진 컨디션 pushdown 최적화 (NDB만 해당)| |Using index condition|인덱스 컨디션 pushdown 최적화 (ICP, 멀티 칼럼 인덱스에서 왼쪽부터 순서대로 칼럼을 지정하지 않아도 인덱스를 사용)| |Using MRR|멀티 레인지 리드 최적화 (MRR)| |Using join buffer(Block Nested Loop)|조인 버퍼를 사용| |Using join buffer(Batched Key Access)|Batched Key Access 알고리즘 (BKAJ)을 위한 조인 버퍼를 사용|


참고

  1. select_type의 UNCACHEABLE SUBQUERY는 캐시된 결과를 사용할 수 없으므로 주의
  2. type의 const는 범위 검색으로 지정한다면 const가 아님
  3. type의 unique_subquery는 오버헤드가 감소함
  4. extra의 Using filesort는 메모리, 디스크 상의 정렬도 모두 포함
  5. type에 all, index가 있다면 좋지 않음
  6. extra에 index가 있다면 괜찮음
  7. 5.0 버전 이후부터는 index_merge 최적화


JSON 형식

EXPLAIN FORMAT = JSON query;
/* Example
{
   "query_block": {
     "select_id": 1,
     "grouping_operation": {
       "using_temporary_table": true,
       "using_filesort": true,
       "nested_loop": [
         {
           "table": {
             "table_name": "b",
             "access_type": "ref",
             "possible_keys": [
               "b_pk",
               "b_ix1"
             ],
             "key": "b_pk",
             "used_key_parts": [
               "evtcode"
             ],
             "key_length": "4",
             "ref": [
               "const"
             ],
             "rows": 8,
             "filtered": 100,
             "attached_condition": "((`testdb`.`b`.`evtcode` <=> 170))"
           }
         },
         {
           "table": {
             "table_name": "a",
             "access_type": "ref",
             "possible_keys": [
               "a_pk"
             ],
             "key": "a_pk",
             "used_key_parts": [
               "evtcode"
             ],
             "key_length": "4",
             "ref": [
               "const"
             ],
             "rows": 216720,
             "filtered": 100,
             "attached_condition": "(`testdb`.`a`.`bosslife` = `testdb`.`b`.`life`)"
           }
         }
       ]
     }
   }
 }
*/