본문 바로가기
카테고리 없음

6장. 실행계획

by #moonyz 2015. 8. 26.

1. id 
     - 단위쿼리 구분자 (SELECT 키워드 단위로 구분)
     - 하나의 SELECT 문장안에서 여러개의 테이블 조인하면 조인되는 테이블의 개수만큼 실행계획 레코드 출력 but, 같은 id부여
     - 하나의 쿼리문장이 3개 단위 SELECT 쿼리로 구성되어 있으면 각 단위쿼리당 다른 id부여됨

2. select_type
     - SELECT 쿼리가 어떤 타입인지 표시

SIMPLE UNION이나 Sub Query를 사용하지 않는 단순한 SELECT 쿼리인 경우 (조인 포함)
PRIMARY UNION이나 Sub Query가 포함된 SELECT 쿼리의 실행계획에서 가장 바깥쪽에 있는 단위 쿼리 (OUTER)
UNION UNION으로 결합하는 단위 SELECT 쿼리 가운데 첫 번째를 제외한 두번째 단위 이후 SELECT쿼리
DEPENDENT UNIONUNION이나 UNION ALL로 집합을 결합하는 쿼리 중 결합된 단위쿼리가 외부의 영향을 받는 쿼리
- 일반적으로 서브쿼리가 사용되는 경우 외부쿼리보다 서브쿼리가 먼저 실행됨
- DEPENDENT 키워드를 포함하는 서브쿼리는 외부쿼리에 의존적이므로 절대 외부쿼리보다 먼저 실행 불가
- 그러므로 DEPENDENT 키워드가 포함된 서브 쿼리는 비효율적인 경우가 많음
UNION RESULTUNION 결과를 담아두는 테이블
MySQL에서 UNION ALL, UNION (DISTINCT) 등의 쿼리는 임시테이블로 생성함
UNION RESULT는 실제 쿼리에서 단위 쿼리가 아니기 때문에 별도의 id값은 부여되지 않음
EX) UNION RESULT <union1,2> : id가 1번인 단위쿼리의 조회결과와 id가 2번인 단위쿼리의 조회 결과를 union
SUBQUERYFROM절 이외에서 사용되는 서브쿼리를 뜻함
DEPENDENT SUBQUERY서브쿼리가 바깥쪽 SELECT 쿼리에서 정의된 컬럼을 사용하는 경우
안쪽의 서브쿼리결과가 바깥쪽 SELECT 쿼리의 컬럼에 의존적
외부쿼리가 먼저 수행된 후 안쪽 쿼리가 실행되어야 하므로 일반서브쿼리보다 처리속도 느림
DERIVED서브쿼리가 FROM절에 사용된 경우
DERIVED는 단위 SELECT쿼리의 실행 결과를 메모리나 디스크에 임시 테이블을 생성 (파생테이블)
파생테이블에는 인덱스가 전혀 없으므로 다른 테이블과 조인할 때 성능상 불리
가능하면 DERIVED형태의 실행계획을 조인으로 해결할 수 있도록 변경해주는 것이 좋음
UNCACHEABLE SUBQUERY서브쿼리가 실행될때 다시사용하는 경우를 대비하여 내부적인 캐시공간에 담아두는 경우 캐시사용불가
subquery : 외부쿼리의 영향을 받지 않으므로 처음 한번 실행하여 그 결과를 캐시에 담아둠
dependent subquery : 의존하는 외부쿼리의 컬럼을 값 단위로 캐시해두고 사용함

서브쿼리에 포함된 요소에 의해 캐시 자체가 불가능한 경우 (p.276)
- 사용자변수가 서브쿼리에 사용된 경우
- NOT-DETERMINISTIC속성의 스토어드 루틴이 서브쿼리내에 사용되는 경우 (p. 
- UUID()나 RAND()와 같은 결과값이 호출될때마다 달라지는 함수가 서브쿼리에 사용된경우
UNCACHEABLE UNIONUNION과 UNCACHEABLE 키워드의 속성이 혼합
MySQL 5.1 부터 추가된 타입

3. table
     - MySQL의 실행 계획은 단위 SELECT 쿼리 기준이 아니라 테이블 기준으로 표시함
     - 테이블의 이름에 별칭이 부여된 경우에는 별칭 표시
     - 별도의 테이블을 사용하지 않는 SELECT쿼리는 NULL 표시됨 EX) SELECT NOW( );
     - "< >" 로 둘러싸인 이름이 명시되는 경우는 임시테이블
     - "< >" 안에 표시되는 숫자는 단위 SELECT쿼리의 id를 의미함 EX) <derived 2> 단위SELECT 쿼리의 id 2번은 파생 테이블

<참고>
- 일부 DBMS에서는 SELECT문장은 반드시 FROM절을 가져야 하는 제약이 있기때문에 DUAL이라는 스칼라값을 가지는 테이블을 사용함
 EX) SELECT NOW( ) FROM DUAL; --오라클
- MySQL에서는 FROM절이 없어도 문제가 되지 않으므로 호환을 위해 FROM DUAL을 사용해도 안해도 무방

- FROM절에 사용된 서브쿼리 (DERIVED)는 반드시 별칭을 가져야 함
 EX) SELECT * 
FROM (SELECT de.emp_no FROM dept_emp de) tb, employees e 
WHERE e.emp_no=tb.emp_no;

4. type
     - type이후의 컬럼은 MySQL 서버가 각 테이블의 레코드를 어떤 방식으로 읽었는지 의미함      
     - 조인타입 : MySQL에서는 하나의 테이블로부터 레코드를 읽는 작업도 조인처럼 처리하기 때문에 테이블 개수에 상관없이 조인타입으로 명시
     - 조인과 연관지어 생각하지 말고 각 테이블의 접근 방식으로 해석하면 됨 (Access Type)
     - 하나의 단위 SELECT쿼리는 아래 접근방법 중 단 하나만 사용가능
     - index_merge를 제외한 나머지 접근방법은 반드시 하나의 인덱스만 사용
     - 성능이 빠른 순서대로 나열함 
system레코드가 1건만 존재하는 테이블 / 한건도 존재하지 않는 테이블을 참조하는 형태의 접근방법
InnoDB 테이블에서는 나타나지않고, MyISAM이나 MEMORY테이블에서만 사용되는 접근방법
const조인의 순서에 관계없이 primary key나 unique key의 모든 컬럼에 대해 동등(Equal)조건 으로 검색 (반드시 1건의 레코드만 반환함)
 * 유니크 인덱스 스캔  (UNIQUE INDEX SCAN)

다중컬럼으로 구성된 primary key나 unique key 중 인덱스의 일부 컬럼만 조건으로 사용할 경우에는 사용불가
- primary key의 일부만 조건으로 사용할때는 접근방식이 ref로 표시됨
- 다중컬럼으로 구성된 인덱스의 모든 컬럼을 동등 조건으로 명시하면 const 접근사용
eq_ref조인에서 처음 읽은 테이블의 컬럼값을 이용해, 그 다음 읽어야 할 테이블의 primary key나 unique key로 동등(Equal)조건으로 검색할때 (반드시 1건 )
여러 테이블이 조인되는 쿼리의 실행계획
조인에서 두번째 이후에 읽는 테이블에서 반드시 1건만 존재한다는 보장
ref조인의 순서와 인덱스의 종류에 관계없이 동등(Equal)조건으로 검색할때 (반드시 1건의 레코드만 반환된다는 보장 없음)
동등(Equal)조건으로 검색할때, 조인의 순서, 프라이머리, 유니크키 등의 제약조건 없음
레코드가 반드시 1건이라는 보장이 없으므로 const나 eq_ref보다는 빠르지않지만 동등조건으로 비교되므로 빠른 조회방법

다중컬럼으로 구성된 primary key나 unique key 중 인덱스의 일부 컬럼만 조건으로 사용할 경우
- 일부 컬럼만 동등조건으로 명시되었기 때문에 반드시 1건이라는 보장이 없음
fulltextMySQL의 전문검색인덱스를 사용해 레코드를 읽는 접근방법
비용기반옵티마이저에 의해 처리성능이 빠른 편이긴 하지만 순서는 달라질 수 있음 (전문검색인덱스는 통계정보가 관리되지 않음)
전문검색은 반드시 해당 테이블에 전문검색용 인덱스가 준비되어 있어야함, 없으면 쿼리 오류발생 후 중지됨
전문검색은 "MATCH...AGAINST..."구문을 사용 EX) WEHRE MATCH(first_name, last_name) AGAINST('Facello' IN BOOLEAN MODE);
ref_or_nullref접근방식과 같은데 NULL비교가 추가된 형태
EX) SELECT * FROM titles WHERE to_date='1985-03-01' OR to_date IS NULL;
unique_subqueryWHERE조건절에서 IN(subquery) 형태의 쿼리를 사용할때 서브쿼리에서 중복되지 않은 유니크한 값만 반환할 때 사용
IN(subquery) 형태의 조건에서 subquery의 반환값에는 중복이 없으므로 별도의 중복제거작업이 필요하지 않음 
EX) SELECT *  FROM departments WHERE dept_no IN (SELECT dept_no FROM dept_emp WHERE emp_no=10001); --서브쿼리에서 유니크한 값 반환
index_subqueryIN(subquery)에서 subquery가 중복된 값을 반환할수 있지만 인덱스를 이용해 제거할수 있을 경우
IN(subquery) 형태의
 조건에서 subquery의 반환값에 중복된 값이 있을 수 있지만 인덱스를 이용해 중복된 값을 제거할수 있음 (아주 낮은 비용으로 제거)

 * IN( ) 연산자의 특성상 괄호안에 있는 값의 목록에서 중복된 값이 먼저 제거되어야 함
range인덱스를 범위로 검색하는 경우 (<, >, IS NULL, BETWEEN, IN, LIKE)
 * 인덱스 레인지 스캔 (=const, ref, range) / 인덱스를 효율적으로 사용한다. = 범위제한 조건으로 인덱스를 사용한다
index_merge2개 이상의 인덱스를 이용해 각각의 검색 결과를 만들어 낸 후 그 결과를 병합하는 처리방식
EX) WHERE emp_no BETWEEN 10001 AND 11000 OR first_name='Smith'; --emp_no는 프라이머리 키를 이용, first_name은 인덱스를 이용
index인덱스를 처음부터 끝까지 읽는 접근방식 
 * 인덱스 풀 스캔 (index Full scan)
일반적으로 인덱스는 데이터파일전체보다는 크기가 작아서 풀 테이블 스캔보다는 효율적, 정렬된 인덱스의 장점 이용 가능

range, const, ref와 같은 방식으로 인덱스를 사용하지 못하는 경우
인덱스에 포함된 컬럼만으로 처리할 수 있는 쿼리인 경우 (데이터파일을 읽지 않아도 되는경우)
인덱스를 이용해 정렬이나 그룹핑 작업이 가능한 경우 (별도의 정렬작업을 하지 않아도 되는 경우 - 인덱스가 정렬되어 있으니까)
ALL테이블을 처음부터 끝까지 전부 읽어서 불필요한 레코드를 제거하고 반환하는 방식 (체크조건이 존재할 때)
index와 all 접근 방법은 작업범위를 제한하는 조건이 아니므로 빠른응답을 보내줘야 하는 웹서비스 등과 같은 OLTP환경에는 적합하지 않음

full table scan, index full scan 등과 같은 대량의 디스크 I/O를 유발하는 작업을 위해 InnoDB는 Read Ahead(리드 어헤드) 기능 제공
* Read Ahead : 연속적으로 인접한 페이지가 연속해서 몇 번 읽히게 되면 백그라운드로 작동하는 읽기 스레드가 최대 한번에 64개의 페이지씩 한꺼번에 디스크로부터 읽어들이기 때문에 한번에 한페이지씩 읽어들이는 작업보다는 상당히 빠르게 레코드를 읽을수 있음

5. possible_keys
     - MySQL 옵티마이저가 최적의 실행 계획을 만들기 위해 후보로 선정했던 접근 방식에서 사용되는 인덱스의 목록
     - 실제로 그 인덱스를 사용하지 않았음, 사용될 법했던 인덱스의 목록
     - 실행계획을 확인할 때 possible_keys 컬럼은 무시하는 것이 좋음

6. key
     - 최종 선택된 실행 계획에서 사용하는 인덱스 (의도했던 인덱스가 표시되는지 확인)
     - PRIMARY KEY 또는 인덱스 생성 시 부여했던 고유 인덱스 이름 출력됨 (Primary key는 별도의 이름을 부여할 수 없음)
     - index_merge 실행 계획이 사용할 경우에만 2개 이상의 인덱스 출력, 나머지 실행계획은 1개의 인덱스만 사용됨
     - 실행계획에서 인덱스를 전혀 사용하지 못하면 (all) key값은 NULL로 표시된다.

7. key_len
     - 다중컬럼으로 구성된 인덱스에서 몇개의 컬럼까지 사용했는지 확인 가능
     - dept_no(char(4))와 emp_no(int)로 만들어진 primary key를 사용할 경우 
          >> key_len값이 12인 경우 : dept_no컬럼만 사용됨, char(1)은 1~3byte까지 가변적이지만 3바이트로 계산하여 3byte X 4 = 12byte
          >> key_len값이 16인 경우 : dept_no과 emp_no컬럼 모두 사용되어 12byte + 4byte = 16byte (int는 4byte 차지함)

8. ref
     - 접근 방법이 ref방식일 때 참조조건(Equal 비교조건) 제공값
     - 상수 값을 지정했다면 ref 컬럼의 값은 const 표시
     - 다른 테이블의 컬럼값을 지정했다면 그 테이블명과 컬럼명 표시
     - func : Function, 참조용 값을 그대로 사용하지 않고 콜레이션 변환이나 값 자체의 연산을 거쳐서 참조했다는 것을 의미함 EX) WHERE e.emp_no=(de.emp_no-1);
     - 사용자가 명시적으로 값을 변환할 때 뿐만아니라, MySQL 서버가 내부적으로 값을 변환해야 할때도 func 출력 (되도록이면 조인 컬럼 타입을 일치)

9. rows
     - 실행 계획의 효율성 판단을 위해 예측했던 레코드 건수
     - 각 스토리지 엔진별로 가지고 있는 통계정보를 참조해 MySQL 옵티마이저가 산출해 낸 예상 값이기 때문에 정확하지 않음
     - rows 컬럼에 표시되는 값은 쿼리를 처리하기 위해 얼마나 많은 레코드를 디스크로부터 읽고 체크해야 하는지를 의미함 (반환하는 레코드의 예측치가 아님)
     - 그러므로, 실행계획의 rows 컬럼에 출력되는 값과 실제 쿼리 결과 반환된 레코드 건수는 일치하지 않는 경우가 많음
     - rows값에 따라서 테이블 스캔 방식이 결정됨(?) --테이블 대부분의 값을 읽어야 한다면 테이블 풀 스캔 등

10. Extra
     - 쿼리의 실행 계획에서 성능에 관련된 중요한 내용이 표시됨
     - 일반적으로 2~3개씩 같이 표시

const row not foundconst 접근 방식으로 테이블을 읽었지만 실제로 해당 테이블에 레코드가 1건도 존재하지 않음
Distinctdistinct를 처리하기 위해 조인하지 않아도 되는 항목은 모두 무시하고 꼭 필요한 것만 조인
(필요한 레코드만 읽음)
Full scan on NULL key


댓글