- MySQL 8.0 부터는 히스토그램 도입
- MySQL 5.7 버전에서 지원했던 테이블과 인덱스에 대한 개괄적인 정보 + 데이터 칼럼의 분포값 정보 -> 더 정확한 실행계획 선택 가능
- 휘발성 VS 영구성
- MySQL 5.6 이전 : 휘발성
- 메모리에만 관리
- SHOW INDEX 명령으로만 테이블의 인덱스 칼럼 분포도를 확인할 수 있었음
- MySQL 5.6 부터 : 영구성
- InnoDB 스토리지 엔진 사용하는 테이블에 대한 통계 정보를 영구적으로 관리할 수 있음
- mysql 데이터베이스의 innodb_index_stats 테이블과 innodb_table_stats 테이블로 관리 가능
- 핵심 : 이제 서버 재시작해도 기존 통계 정보 유지할 수 있게 되었다
STATS_PERSISTENT옵션 : 영구적으로 통계 정보를 보관할지 결정
- MySQL 5.6 이전 : 휘발성
- 통계 정보 갱신 (398p)
- MySQL 5.6 이전 : 자동 갱신
- 통계 정보가 갱신되면 쿼리 실행 계획 갑자기 달라질 수 있음
- 통계정보를 바탕으로 실행계획을 수립하기 때문 MySQL 서버가 갑자기 실행 계획을 다르게 설정(예측 불허)
- MySQL 5.6 부터 : 수동 갱신 가능 + 자동일지도 선택가능
- 통계 정보 자동 수집 여부 결정 가능 by
STATS_AUTO_RECALC옵션 - 통계 정보 수집 시, 몇 개의 페이지 샘플링 할 것인지도 설정 가능 by 시스템 변수
- 갯수 늘리기 -> (+) 더 정확한 통계 정보 수집 가능 -> (-) 수집 시간 길어짐
innodb_stats_transient_sample_pages: 8 기본 / 샘플링 분석 후 통계 정보로 활용innodb_stats_persistent_sample_pages: 20 기본 / 샘플링 분석 후 통계 정보 테이블에 저장 + 활용
- 통계 정보 자동 수집 여부 결정 가능 by
- MySQL 5.6 이전 : 자동 갱신
- MySQL 5.7 버전까지는
- 인덱스된 컬럼의 유니크한 값 개수 + 실제 인덱스의 일부 페이지 랜덤 get
- MySQL 8.0 버전부터는 히스토그램 정보 활용 가능
-
수집
- 히스토그램 정보는 컬럼 단위로 관리
- 수동 수집 - by
ANALYZE TABLE ... UPDATE HISTOGRAM - (평소에는) 시스템 딕셔너리에 저장
- (MySQL 서버 실행시) information_schema 데이터베이스, column_statistics 테이블로 로드해서 사용
-
히스토그램 타입 (401p)
- 싱글톤 히스토그램 : 칼럼값 개별 레코드 건수 기준
- 버킷 단위는 칼럼이 가지는 값
- 유니크한 값의 개수가 적은 경우 사용 -> 레인지 스캔인지 확인하는데 유용하지 않을까
- 높이 균형 히스토그램 : 컬럼값의 범위를 균등한 개수로 구분해서 관리
- 버킷 단위는 개수가 균등한 컬럼값의 범위
- 싱글톤 히스토그램 : 칼럼값 개별 레코드 건수 기준
-
[주의] 403 p 주의 참고 : 히스토그램 수집 시, 풀스캔 조심 (MySQL 8.0.19 미만의 경우)
-
삭제
- 쿼리 처리의 성능에 영향 X -> 테이블 데이터를 참조하지 않고 딕셔너리 내용만 삭제하므로
- 실행 계획에 영향 O -> 실행 계획 수립시 사용하던 히스토그램이 삭제되었으므로
- MySQL 옵티마이저가 히스토그램 사용하지 않게하는 옵션 사용해서 삭제 시 발생하는 실행 계획 변경에 미리 대비는 가능 (특정 커넥션/쿼리 등 범위를 한정지어서 설정도 가능)
-
히스토그램의 용도
- (before) 테이블과 인덱스에 대한 통계정보만으로 실행계획 수립
- (after) 테이블과 인덱스에 대한 통계정보 + 히스토그램의 분포도 정보
- (한 번 더 리마인드 > 히스토그램도 추정치임 특정 페이지만을 샘플링한 정보)
- 실행 계획 정확도 up (405p 쿼리 참고)
- 조인 순서 결정에 영향 -> 어느 테이블을 먼저 읽어야 조인 횟수를 줄일 수 있는지 정확한 판단 가능 (406p 쿼리 참고)
-
히스토그램 & 인덱스
- 인덱스 다이브 - 실제 인덱스의 B-tree를 샘플링해서 본다
- 인덱스 다이브 통해 사용 가능한 인덱스들을 보고, 조건절에 일치하는 레코드 건수를 대략적으로 파악하는데 사용
- 인덱스 다이브 - 실제 인덱스의 B-tree를 샘플링해서 본다
-
인덱스 다이브는 언제 사용?
- MySQL 8.0 서버부터는 인덱스된 컬럼을 검색 조건으로 사용하는 경우, 히스토그램이 아닌 인덱스 다이브를 활용
- 즉, 히스토그램은 인덱스되지 않은 칼럼에 대해서만 사용하는 편
-
인덱스 다이브의 단점?
- 비용
- 특히 IN절이 늘어나면 실행 계획 수립만으로도 인덱스 다이브 실행 비용 크다
- (우디 추측) 인덱스 다이브는 그때그때 만들어지나보다
- 비용
- 코스트 모델(Cost Model) : 전체 쿼리의 비용을 계산하는데 필요한 단위 작업들의 비용
- MySQL 5.7 버전까지는
- MySQL 서버 소스코드에 상수화
- 하드웨어 따라 달라질 수 있는 점은 고려 X
- MySQL 5.7 이후부터는
- 각 단위 작업의 비용을 서버 밖에서 관리 (DB 관리자가 컨트롤 가능)
- 409 페이지 참고, 코스트 모델의 어떤 단위 작업들에 대해서 비용 설정 가능한지 표기되어있음
- 작업 비용 높게 산정하면 그만큼 해당 단위 작업들에 쓰는 비용 증가
- MySQL 8.0 이후부터는,
- 한 번의 EXPLAIN 명령으로 PARTITIONS, EXTEND 다 조회 가능
- PARTITIONS 과 EXTEND 옵션은 문법에서 안녕~
- FORMAT 옵션
- 실행 계획 표시 방법을 선택 가능 - JSON, TREE
- 한 번의 EXPLAIN 명령으로 PARTITIONS, EXTEND 다 조회 가능
EXPLAIN ANALYZE기능- 쿼리 실행 계획 확인
- 실행 단계별로 소요된 시간 정보 확인 가능
EXPLAIN과 다르다 :EXPLAIN ANALYZE는 실제 쿼리를 실행하고 사용된 실행계획과 소요된 시간을 보여줌
- 실행 순서 기준
- 들여쓰기가 같은 레벨 -> 상단에 위치한 라인 먼저
- 들여쓰기 다른 레벨 -> 가장 안쪽 라인 먼저
EXPLAIN ANALYZE요소 (공식 문서 참고)- actual time : {첫 번째 레코드 검색하는데 걸린 시간}...{마지막 레코드 검색하는데 걸린 시간}
- rows : 처리한(읽은) 레코드 갯수
- loops : 작업이 반복된 횟수
- 414p 예시 함께 잠깐 보기
- (우디 자문자답) 왜 F가 가장 먼저 실행되는게 아닐까
- F 구문이 D에 종속적이라서 그런 듯하다
-
focus on
- 실행 계획이 어떤 접근 방법을 사용해서 어떤 최적화 수행하는지
- 어떤 인덱스 사용하는지
-
EXPLAIN 테이블
- 표의 각 라인(레코드)
- 쿼리 문장에서 사용된 테이블 (+ 임시테이블도 포함) 출력
- 실행 순서대로 출력 (UNION, 상관 서브 쿼리 제외)
- (+ 상관 서브쿼리 : 내부 쿼리의 값이 결정되는데 외부 쿼리에 의존하는 쿼리)
- 위일 수록 outer 혹은 먼저 접근한 테이블, 아래쪽일 수록 inner 혹은 나중에 접근한 테이블
- 표의 각 라인(레코드)
- 단위 SELECT 쿼리별로 부여되는 식별자
- (+ 단위 쿼리 = SELECT 키워드 단위로 구분한 쿼리)
- 여러 개의 레코드가 같은 id 가질 수 있다
- 하나의 SELECT 문장 내 여러 개의 테이블을 조인하면, 조인되는 테이블 개수만큼 실행 계획 레코드가 출력, BUT 같은 id 값 가짐
- id 칼럼 != 테이블 접근 순서
- 단위 SELECT 쿼리가 어떤 타입의 쿼리인지 표시됨
- UNION이나 서브 쿼리 사용하지 않는 단순 SELECT 쿼리
- 일반적, 가장 바깥 SELECT 쿼리
- UNION이나 서브 쿼리 사용하는 SELECT 쿼리의 실행계획에서 가장 Outer 단위 쿼리
- UNION으로 결합하는 단위 SELECT 쿼리 중
- 첫 번째 : DERIVED
- 첫 번째 이후 단위 쿼리 : UNION
- (우디) Union 키워드 사용 시, 내부적으로는 임시 테이블 사용하는 구나
- 집합을 결합하는 단위 쿼리(UNION, UNION ALL)에서 외부 쿼리에 의해 영향을 받을 경우
- 상관 서브쿼리일 경우, DEPENDENT 키워드 표시
- UNION의 결과를 담아두는 임시 테이블
- 단위 쿼리가 아니므로 별도 id 값은 X
- <union 1, 2> : id 값 1와 2 단위쿼리 결과를 조합
- MySQL 8.0 이전 : UNION ALL 혹은 UNION 쿼리는 결합 결과를 모두 임시 테이블로 생성
- MySQL 8.0 이후 : UNION ALL 사용하지 않도록 변경됨 / UNION 여전히 임시 테이블 생성
- 위치에 따른 서브 쿼리 명
- 중첩된 쿼리 : SELECT 절
- 서브쿼리 : WHERE 절
- 파생 테이블 : FROM 절 (= 인라인뷰 = 서브 셀렉트)
- 반환 값 개수
- 스칼라 서브쿼리 : 컬럼 하나인 레코드 1건의 값 1개만 반환
- 로우 서브쿼리 : 컬럼 개수와 상관없이 하나의 레코드만 반환
- 여기서의 SUBQUERY는 FROM절 이외에서 사용되는 서브쿼리만을 말한다
- (FROM절에서 사용되는 서브쿼리 = DREIVED 표시)
- 서브쿼리가 Outer SELECT 쿼리에서 정의된 칼럼을 사용하는 경우
- 일반적인 서브쿼리보다 처리속도 느리다
- 외부 쿼리 먼저 수행된 후 내부 쿼리 실행되어야 하므로
- MySQL 5.6 이전
- 서브쿼리 FROM 절 -> 임시 테이블 만든다 -> DERIVED 실행 계획
- MySQL 5.6 이후
- FROM 절의 서브쿼리를 외부 쿼리와 통합하는 식의 최적화로 인해 반드시 DERIVED 실행 계획인건 아니게 됨
- 임시 테이블에서도 인덱스 추가해서 만들 수 있게 됨 (옵티마이저 옵션별로 상이)
- 서브쿼리 있으면 최대한 조인으로 풀어보자고 또 한 번 다짐..
- MySQL 8.0 이전
- FROM 절 서브쿼리에서 외부 칼럼 사용 X
- MySQL 8.0 이후
- lateral join을 사용하면 가능
- lateral join 설명 링크
- 캐시를 사용할 수 없는 서브쿼리를 사용하는 경우
- 원래는 이전의 실행 결과를 그대로 사용할 수 있도록 서브 쿼리 결과를 내부 캐시에 저장
- 서브쿼리 캐시 != 쿼리 캐시 혹은 파생 테이블
- 캐시 사용 방법
- SUBQUERY : 한 번 실행에 대한 결과를 캐시하고, 필요할 때 사용
- DEPENDENT SUBQUERY : 의존하는 바깥 쿼리의 칼럼 값 단위로 캐시해둔다 (?? 정확한 과정은 잘 모르겠어욥)
- 캐시 사용하지 못하게 하는 요소들 3가지 (428p)
- 캐시를 사용할 수 없는 유니언 쿼리 사용하는 경우
- MySQL 5.7 이전
- outer 테이블 레코드마다 서브 쿼리 실행
- MySQL 5.7 이후
- FROM절 혹은 IN(subquery) 형태의 쿼리에 대해서, 서브쿼리 내용을 임시 테이블로 구체화한 뒤 outer 테이블과 조인하는 형태로 최적화
- Derived 와 동일하게 작동, 서브쿼리의 위치가 다른 것일 뿐임
- MySQL 서버의 실행 계획은 테이블 기준
- <> 표시는 임시 테이블을 의미, 안의 숫자는 단위 SELECT 쿼리 id 값을 지칭
- MySQL 8.0 버전부터는 EXPLAIN 명령으로 파티션 관련 실행 계획까지 한 번에 확인 가능
- 파티션 프루닝 : 파티션이 여러 개인 테이블에서 불필요한 파티션을 빼고 쿼리를 수행하도록, 테이블을 골라내는 과정
- 옵티마이저에서는 쿼리의 조건들을 확인하고, 필요한 파티션만 접근하는 파티션 프루닝으로 쿼리 최적화 시도
- (+) 대부분 RDB에서 지원하는 파티션은 물리적으로 별도의 공간을 가진다 (그러니, 실행 계획 상으로는 type ALL로 뜰 수 있으니 놀라지 말 것. 우리들이 생각하는 풀스캔 타는거 아님)
- 인덱스를 효율적으로 사용하는지 확인할 수 있는 컬럼
- type 컬럼 == 조인 타입... 이라고 되어있지만 '각 테이블의 접근 방법'이라고 생각해도 좋음
- 레코드가 1건 혹은 아예 존재하지 않는 테이블 참조
- MyISAM 혹은 MEMORY 테이블에서만 사용
- 프라이머리 키 혹은 유니크 키 칼럼을 이용하는 WHERE 조건절을 가지고 있고 + 반드시 1건을 반환하는 쿼리만을 처리
- 유니크 인덱스 스캔이라고도 함
- 일부 칼럼만 조건을 사용할 때는 X -> const가 아닌 ref
- 옵티마이저의 쿼리 최적화 단계에서 const인 실행 계획은 먼저 쿼리를 실행해서 통째로 상수화한다 (우디 : 그래서 반드시 1건을 반환해야하는군!)
- 여러 테이블이 조인되는 쿼리 실행 계획에서만 표시
- 첫 번째 테이블 컬럼값을 두 번째 조인되는 테이블의 PK 혹은 유니크 키 컬럼의 검색조건(조인 조건)으로 사용할 때
- 두 번재 조인 테이블의 type 컬럼 eq_ref
- 유니크 키로 검색할 경우 NOT NULL이어야 함
- 조인에서 두 번째 이후에 읽는 테이블에서 반드시 1건만 존재한다는 보장이 있어야 함
- 조인의 순서와 관계 없이 사용 + PK 혹은 유니크 키 제약 조건도 X
- 반드시 1건이라는 보장이 없음 (eq_ref가 더 빠름)
- 동등 조건으로만 비교하기 때문에 여전히 빠름
const, eq_ref, ref
- 동등 비교 연산자 사용
- 쿼리 튜닝시 나오면 좋은 type
- MySQL 서버의 전문 검색 인덱스를 사용해 레코드를 읽는 접근 방식
- 전문 검색 인덱스는 통계정보 관리 X
MATCH (..) AGANIST (..)구문으로 전문 검색 실행
- ref 접근 방법 + null 비교 추가
- 나쁘지는 않다 (ㅋㅋ)
- Where 조건절에서 사용되는 IN(서브 쿼리)를 위한 접근 방식
- 서브쿼리에서 중복되지 않는 유니크한 값만 반환할 때
- IN(서브 쿼리) 결과에서 중복된 값이 발생했을 때, 인덱스로 중복된 값을 제거할 수 있음
- 인덱스 레인지 스캔 형태의 접근 방법
- 범위 형태로 검색하는 경우 적용 (< >, is null, between, in, like)
인덱스를 효율적으로 사용한다
- const, ref, range 사용한다 (우디 질문 : const는 왜 레인지 스캔일까)
- 2개 이상의 인덱스를 이용 -> 검색 결과 각각 만듦 -> 병합
- (-) 여러 인덱스 읽어야 함
- (-) 전문 검색 인덱스 사용 쿼리는 적용 안됨
- (-) 검색 결과를 모아주는 비용이 필요 (교집합, 합집합, 중복제거 등등)
- 인덱스 풀 스캔
- 비교 레코드 건수는 테이블 풀 스캔과 동일, BUT 데이터 파일 크기가 다름
- 풀 테이블 스캔
- 가장 비효율적인 방법이지만, '쿼리 튜닝을 한다'는 것이 반드시 인덱스 혹은 테이블 풀 스캔을 사용하지 못하게 하는건 아님 (우디 : 정확하게 이해는 못함)
- 사용될법했던 인덱스의 목록
- 그냥 무시하자
- possible_keys 에 적힌 인덱스를 사용했다고 판단하지 말자
최종 실행 계획에서 사용하는 인덱스
- 인덱스 각 레코드에서 몇바이트까지 사용했는지 알려줌
- 다중 칼럼 인덱스 분석에 유용함
- NULLABLE 칼럼일 경우, NULL 판단 여부를 위해 1 바이트가 추가됨
- equal 비교로 인한 참조조건으로 어떤 테이블의 칼럼이 제공됐는지 표시
- 상수값이라면 const 가 표시됨
- func 일 경우 Function 을 통해 연산된 값
- 사용자가 직접 Function 을 하지 않아도 MySQL 내부적으로 func 일 경우가 있음
- 대표적으로 숫자 타입을 문자 타입 칼럼으로 join 할 때
- func 이 되지 않도록, 조인 칼럼을 일치시키는 편이 좋음
- 옵티마이저가 예측한 레코드 건수
- 따라서 실제 레코드 건수와 일치하지 않는다.
- WHERE 절을 통해 필터링되고 남은 레코드의 비율
- MySQL 8.0 부터 히스토그램을 통해 더 정확해짐
- 필터링이 스토리지 엔진이 아니라 MySQL 엔진에서 이루어진다.
- 즉 비율이 높으면 버려지는 row 가 많다는 뜻이다.
- 스토리지에서 디스크를 적게 읽을 수 있도록 최적화가 필요하다.
성능에 관련된 중요한 내용이 표시되므로 알아둘 필요가 있다.
const 방법으로 읽었지만, 테이블에 레코드가 1건도 존재하지 않으면 표시됨
DELETE 의 실행계획에서, 모든 레코드를 삭제하는 핸들러 API 를 호출했음을 뜻함
중복 없이 꼭 필요한 것만 읽어왔다는 것을 뜻함
- FirstMatch 세미조인 최적화 전략을 사용했다는 것을 뜻함
- 9.3.1.11절 p.340 그림 참조
- SQL 표준에서 NULL 은 알수없는 값
- IN 혹은 NOT IN 연산에서 왼쪽 값이 NULL 인 레코드가 있고, 서브쿼리에 개별적으로 WHERE 가 지정되어 있을 경우
- Full Table Scan 이 발생해서 심각한 성능 문제가 일어날 수 있음
- 하지만 왼쪽값이 NULL 이 없다면, Full scan on NULL key 가 표시되어도 걱정안해도 됨
HAVING 절의 조건을 만족하는 레코드가 없을 때 표시
WHERE 조건이 논리적으로 항상 FALSE 가 되는 경우 표시
- Loose 세미조인 최적화 전략이 사용되었음을 의미
- 9.3.1.12절 p.342 그림을 참고
- MIN, MAX 연산을 할 때 WHERE 을 만족하는 레코드가 없을 때 표시
- 이때 MIN 과 MAX 는 NULL 을 반환
- 그 외에는 Impossible WHERE 가 표시됨
- Impossible... 랑 No matching... 은 잘못된 Extra 가 아니니까 당황하지 말자!
- 대신에 기획이 문제가 있는 경우가 많으므로 기획자를 혼내자
const 방법으로 접근할 때 일치하는 레코드가 없으면 표시
- 파티션된 테이블에서 UPDATE, DELETE 시 대상 레코드가 없을 때 표시됨
- 실제로는 레코드가 없다는 의미가 아니라, 대상 파티션이 없다는 것을 의미
- DUAL 테이블이 사용될 때 표시
- DUAL 은 칼럼과 레코드를 1개씩 가지는 가상의 상수 테이블을 뜻함
- 사실 모든 쿼리는 FROM 이 필요하지만, MySQL 은 dual 이 존재해서 상수를 쓸 수 있는 것처럼 보임
- OUTER JOIN 을 이용해 ANTI-JOIN을 수행할 경우 표시
- ANTI-JOIN: INNER JOIN 을 했을 때 나오지 않는 결과를 가져오는 걸 뜻함
- 옵티마이저가 최적화를 잘했다는 것을 의미
- MySQL 8.0 은 실행 중인 쿼리의 실행 계획을 살펴볼 수 있음
- 아직 실행 계획을 수립 못했다는 것을 뜻함
- 레코드마다 인덱스 레인지 스캔을 체크할 때 표시
- type 칼럼에 ALL 이라고 표시되어도 당황치 말자, 후보 인덱스가 별 도움이 안되어서 풀 테이블 스캔을 한 것이다.
- 각 레코드 단위로 여러 후보 인덱스 중에서 실제 어떤 인덱스가 사용됐는지는 알 수 없다.
- WITH RECURSIVE 문법을 사용할 경우 표시
- MySQL 8.0 부터 CTE(Common Table Expression) 이 도입됨
- MySQL 8.0 부터 LATERAL JOIN 도입
- LATERAL JOIN 시, 레코드별로 서브쿼리 실행한 결과를 임시 테이블에 저장함
- 임시 테이블에 저장하는 과정을 Rematerializing 이라고 함
- 책 2권 11.4.7.6 절에 자세히 나온다고 함
- MIN, MAX, GROUP BY 시
- 인덱스를 오름차순 또는 내림차순으로 1건만 읽을경우 적용되는 최적화
- Duplicate Weed-out 세미 조인 최적화를 할 경우 표시
- 9.3.1.14 절 p346 참고
- 유니크 칼럼으로 아우터 조인을 할 때, 아우터 테이블에 일치하는 레코드가 존재하지 않을 때 표시
- ORDER BY 처리가 인덱스를 사용하지 못할 때 표시
- 많은 부하를 일으키므로 튜닝이 필요
- 책 2권 11.4.9 에서 자세히 다룰 예정
- 데이터 파일을 전혀 읽지 않고, 인덱스만 읽어서 쿼리를 모두 처리할 수 있을 때 표시
- 이렇게 인덱스만으로 쿼리를 실행하는 것을 Covering Index 라고 함
- 매우 빠르다는 것을 뜻하니깐, 표시되면 행복해하면 될 것 같다.
- type 칼럼의 index 는 풀 스캔을 의미하므로 헷갈리지 말자
- Index condition pushdown 최적화를 할 경우 표시
- 9.3.1.3절 p.325 참조
- GROUP BY 처리에 인덱스를 이용했음을 뜻함
- GROUP BY 처리시 인덱스 읽는 방법을 Loose Index Scan 이라고 함
- AVG, SUM, COUNT 처럼 모든 인덱스를 다 읽는 것은 Loose 하지 않아서 그냥 Using Index 만 표시됨
- MIN, MAX 처럼 모든 레코드를 다 읽을 필요가 없을 경우 Loose Index Scan 이 적용됨
- 그럴 경우 Using Index for group-by 가 표시됨
- GROUP BY 뿐만 아니라 WHERE 절에서 사용하는 인덱스에 따라, GROUP BY 시 인덱스를 안 탈 수도 있음
- WHERE 조건절이 없는 경우
- Loose Index Scan 을 사용할 조건을 갖추면 발동
- WHERE 조건절이 있지만, 검색을 위해 인덱스를 사용하지 못한 경우
- 발동안함. Tight Index Scan 발동
- WHERE 조건절이 있으며, 검색을 위해 인해 인덱스를 사용하는 경우
- WHERE 과 GROUP BY 가 똑같은 인덱스를 사용할 수 있어야 Loose Index Scan 발동
- WHERE 조건절이 없는 경우
- Index Skip Scan 최적화를 할 경우 표시
- 8.3.4.4 절 p.238 참고
- join 시 Driven 테이블의 인덱스를 탈 수 없다면, Block Nested Loop Join 혹은 Hash Join 이 사용됨
- 그럴 경우 Join Buffer 가 사용됨
- Using join buffer(Block Nested Loop)
- Using join buffer(Batched Key Access)
- Using join buffer(hash join)
- MRR(Multi Range Read) 최적화를 할 경우 표시
- MySQL 엔진이 스토리지 엔진에게 넘겨주는 키 값을 정렬해서 최소한의 페이지 접근이 일어나도록 함
- 디스크 접근을 최소화 하는 최적화 기법
- 자세한 내용은 9.3.1.1 p.320 참조
index_merge 시, 2개 이상의 인덱스가 동시에 사용될 경우 표시
이산수학 때 (AND, INTERSECT), (OR, UNION) 이 어떻게 연결되는지 배우므로 설명은 생략한다.
- Using intersect
- 인덱스들이 AND 조건으로 연결됨
- Using union
- 인덱스들이 OR 조건으로 연결됨
- Using sort_union
- OR 연결이 많을 경우, PK 먼저 읽어서 정렬 후 병합했음을 뜻함
- 임시 테이블을 사용할 때 표시
- 임시 테이블이 메모리에 있는지, 디스크에 있는지는 모름
- Using temporary 가 표시 안되도 임시 테이블을 사용했을 수도 있음
- 스토리지 엔진이 아니라, MySQL 엔진에서 필터링이 있었음을 뜻함
- 만약 filtered 칼럼의 값이 높다면, 스토리지 엔진에서 가져온 많은 데이터가 쓸모 없었다는 것을 뜻함
- filtered 칼럼의 값이 높다면, 디스크를 더 적게 읽도록 최적화가 필요함
- 쿼리 마지막에
LIMIT 0을 하면 결과의 메타데이터만 읽을 수 있음
