컴퓨터 과학/[Study] Real MySQL 8.0

Chapter 08.03 B-Tree 인덱스

계란💕 2022. 12. 24. 00:42

8.3 B-Tree 인덱스

  • B-Tree는 칼럼의 원래 값을 변형하지 않고 인덱스 구조체 내에서는 항상 정렬된 상태로 유지한다. 
  • 전문 검색 같은 특수 상황이 아닌 경우, 대부분 B-Tree를 사용한다.
  • B-Tree는 데이터베이스 인덱싱 알고리즘 가운데 가장 일반적으로 사용되고, 가장 먼저 도입된 알고리즘이다.
  • 현재도 가장 범용적인 목적으로 사용된다.
  • 일반적으로 B+-Tree, B*-Tree 가 사용된다.
  • B-Tree의 B는 "balanced"를 의미한다.

 

 

8.3.1 구조 및 특성

  • B-Tree는 트리 구조에서 최상위에 하나의 루트노드가 존재하고 그  하위에 자식 노드가 붙어 있는 형태이다. 
  • 트리 구조에서 가장 하위의 노드: 리프 노드(leaf node) 
  • 루트 노드도 아니고 리브 노드도 아닌 노드: 브랜치 노드(branch node)
  • 아래 그림과 같이 인덱스의 키 값은 모두 정렬되어 있지만 데이터 파일의 레코드는 정렬돼있지 않고 임의의 순서로 저장돼 있다. (항상 insert 된 순서대로 저장되지는 않는다.)
    • 이유: 테이블에서 어떤 레코드가 삭제되서 빈 공간이 생기면 그 공간에 새로운 데이터가 들어가도록 DBMS가 설계되기 때문이다. 

B-Tree index 구조

 

  • 인덱스는 테이블의 키 컬럼만 가지고 있으므로 나머지 컬럼을 읽으려면 데이터 파일에서 해당 레코드를 찾아와야한다. 
  • 이를 위해 인덱스의 리프 노드는 데이터 파일에 저장된 레코드의 주소를 가진다. 
  • 아래의 두 그림은 인덱스의 리프 노드와 데이터 파일 둘의 관계를 나타낸다. 
  • MyISAM
    • 레코드 주소: MyISAM 테이블의 생성 옵션에 따라 레코드가 테이블에 INSERT된 순번이거나 데이터 파일 내의 위치(offset )이다.
  • InnoDB
    • InnoDB 스토리지 엔진을 사용하는 테이블에서는 PK가 ROWID의 역할을 한다. 
  • 두 테이블의 차이점: 세컨더리 인덱스를 통해 데이터 파일의 레코드를 찾아가는 방법이 다르다. 
    • MyISAM: 세컨더리 인덱스가 물리적 주소를 가진다.
    • InnoDB: 논리적인 주소를 가진다. (PK를 주소처럼 사용하기 때문) 따라서, MyISAM과 다르게 데이터 파일을 바로 찾아가지 못한다. 
      • 과정: 인덱스에 저장된 PK 값을 이용해서 PK 키 인덱스를 한번더 검색한 뒤, 리프 페이지에 저장됀 레코드를 읽는다. 즉, InnoDB에서는  모든 세컨더리 인덱스 검색에서 데이터 레코드를 읽기 위해서 반드기 프라이머리 키를 저장하고 있는 B-Tree 를 다시 한번 검색해야한다. 

B-Tree 리프노드와 테이블 데이터 레코드(MyISAM)

B-Tree 리프 노드와 테이블 데이터 레코드 (InnoDB)

 

 

 

 

8.3.2 B-Tree 인덱스 키 추가 및 삭제

 

8.3.2.1 인덱스 키 추가

  • 새로운 키 값이 B-Tree에 저장될 때, 테이블의 스토리지 엔진에 따라 새로운 키 값이 즉시 인덱스에 저장될 수도 있고 그렇지 않을수도 있다. 
  • 저장될 때 저장될 키 값을 이용해서 B-Tree에 저장될 때 저장될 키 값을 이용해서 B-Tree에 상의 적절한 위치를 검색해야한다.
  • 위치가 결정되면 레코드 키 값과 대상 레코드의 주소 정보를 B-Tree의 리프 노드에 저장한다. 
  • 리프 노드가 꽉 차면 리프 노드가 분리(split)되어야 하는데 이는 상위 브랜치 노드까지 처리 범위가 넓어진다.
  • 따라서,  B-Tree는 상대적으로 쓰기 작업(새 키 추가)에 비용이 많이 든다.
  • MyISAM 이나 Memory 스토리지 엔진을 사용하는 테이블에서는 INSERT 문장이 실행되면 새로운 키 값을 즉시 B-Tree인덱스에 변경한다. 
  • 반면에 InnoDB 스토리지 엔진은 인덱스 키 추가 작업을 지연시켜 나중에 처리할 수 있다. 

 

 

8.3.2.2 인덱스 키 삭제

  • B-Tree 키 값이 삭제되는 경우는?
    • 해당 키 값이 저장된 B-Tree의 리프 노드를 찾아서 그냥 삭제 마크만 하면 삭제된다.
    • 삭제 마킹된 공간은 방치하거나 재활용할 수 있다. 
    • 인덱스 마킹 작업 또한 디스크 쓰기가 필요하므로 디스크 I/O가 필요하다.
  • MyISAM 이나 Memory 스토리지 엔진의 테이블에서는 체인지 버퍼와 같은 기능이 없으므로 인덱스 키 삭제가 완료된 후, 쿼리 실행이 완료된다.

 

 

8.3.2.3 인덱스 키 변경

  • 인덱스의 키 값은 그 값에 따라 저장될 리프 노드의 위치가 결정되므로 B-Tree의 키 값이 변경되는 경우에는 단순히 인덱스상의 키 값만 변경하는 것은 불가능하다. 
  • B-Tree 키 값 변경 과정: 키 값은 삭제한 다음 새로운 키 값을 추가하는 형태이다. 
  • InnoDB 스토리지 엔진을 사용하는 테이블에 대해서는 이 작업 모두 체인지 버퍼를 활용해서 지연 처리된다.

 

참고) 체인지 버퍼 https://oranthy.tistory.com/426

 

 

 

8.3.2.4 인덱스 키 검색

  • INSERT, UPDATE, DELETE 작업 시, 인덱스 관리에 따르는 추가 비용을 감당하며서 인덱스를 구축하는 이유는 ?
    • "빠른 검색"을 위해서이다.
  • 인덱스 트리 탐색: 인덱스를 검색하는 작업은 B-Tree의 루트 노드부터 시작해서 브랜치 노드를 거쳐 최종 리프 노드까지 이동하면서 비교 작업을 수행한다. 
    • SELECT, UPDATE, DELETE에서 인덱스 트리 탐색을 사용한다. 
  • B-Tree 인덱스를 이용한 검색은 100% 일치 또는 값의 앞 부분(left most part)만 일치하는 경우에 사용할 수 있다. 
  • 부등호 비교 조건에서도 인덱스를 활용할 수 있지만 인덱스를 구성하는 키 값의 뒷부분만 검색하는 용도로는 인덱스를 사용할 수 없다. 
  • 중요 - 인덱스의 키 값에 변형이 가해진 다음 비교되는 경우는  B-Tree의 빠른 검색 기능을 사용할 수 없다. 
    • 이유: 이미 변형된 값은 B-Tree 인덱스에 존재하는 값이 아니다. 따라서, 함수, 연산을 수행한 결과로 정렬하거나 검색하는 작업은 B-Tree의 장점을 이용할 수 없으므로 주의한다.
  • InnoDB 스토리지 엔진에서의 인덱스
    • 레코드 잠금이나 넥스트 키락(갭락)이 검색을 수행한 인덱스를 잠근 후, 테이블의 레코드를 잠그는 방식으로 구현돼 있다.
    • 따라서, UPDATE, DELETE 문장이 실행될 때, 테이블에 적절히 사용할 수 있는 인덱스가 없으면 불필요하게 많은 레코드를 잠금다. 

 

 

 

8.3.3 B-Tree 인덱스 사용에 영향을 미치는 요소

  • B-Tree 인덱스는 인덱스를 구성하는 칼럼의 크기와 레코드 건수, 그리고 유니크한 인덱스 키 값의 개수 등에 의해 검색이나 변경 작업의 성능이 영향을 받는다. 

 

 

 

8.3.3.1 인덱스 키 값의 크기

  • InnoDB 스토리지 엔진은 디스크에 데이터를 저장하는 기본 단위를 페이지(Page) 또는 블록(block)이라고 한다. 
  • 디스크의 모든 읽기, 쓰기 작업의 최소 작업 단위가 된다. 
  • 페이지는 InnoDB 스토리지 엔진의 버퍼 풀에서 데이터를 버퍼링하는 기본 단위이다.
  • 위의 그림 B-Tree index 구조에서 루트 노드, 브랜치 노드, 리프 노드, 데이터 파일을 구분하는 기준이 페이지 단위이다. 

 

 

8.3.3.2 B-Tree 깊이(depth)

  • 깊이는 중요하지만 직접 제어할 방법은 없다. 
  • 키 값이 16바이트인 경우, 최대 2억(585 * 585 * 585) 개 정도의 키 값을 담을 수 있지만 32 바이트로 늘어나면 5천만(372 * 372 * 372)개로 줄어든다. 
  • B-TRee 깊이는 MySQL에서 값을 검색할 때 몇 번이라 랜덤하게 디스크를 읽어야하는지와 직결되는 문제이다. 
  • 결론: 인덱스 키 값의 크기가 커지면 커질수록 하나의 인덱스 페이지가 담을 수 있는 인덱스 키 값의 개수가 적어진다.
    • 따라서, 같은 레코드 건수라고 하더라고 B-Tree의 깊이가 싶어져서 디스크 읽기가 더 많이 필요한다.
    • 가능하면 인덱스 키 값의 크기를 작게 만든다.

 

 

 

8.3.3.3 선택도(Selectivity, 기수성 Cardinality)

  • 모든 인덱스 키 값 가운데 유니크한 값의 수를 의미한다. 
  • 인덱스 키 값 중에서 중복값이 많을수록 기수성은 낮아진다. 
  • 인덱스는 선택도가 높을수록 검색 대상이 줄어들기 때문에 그만큼 빠르게 처리된다.

 

  Ex) 유니크한 값의 개수는 인덱스나 쿼리의 효율성에 미치는 영향

  • 전체 레코드 수: 10000건
  • case A) country 칼럼의 유니크한 값의 개수가 10개
  • case B) country 칼럼의 유니크한 값의 개수가 1000개
  • country 컬럼에만 인덱스가 있다고 가정
  • 국가와  도시가 중복되는 경우는 없다고 가정
SELECT *
FROM tb_test
WHERE country = 'KOREA' AND city = 'SEOUL';

 

 

  • 쿼리를 실행하면 A는 평균 1000건, B는 평균 10건이 조회될 수 있다. 
  • 만약 찾으려는 칼럼이 1건인 경우, A는 불필요하게 999개를 더 검색한 것이다. 따라서 A 는 부적합
  • 그래서 A케이스의 경우 country 칼럼에 생성된 인덱스를 비효율적이다. 
CREATE TABLE tb_city(
    country VARCHAR(10), 
    city VARCHAR(10), 
    INDEX ix_country (country)
);

 

  • case A) country 칼럼의 유니크한 값의 개수가 10개
    • 전체 레코드 건수 / 유니크 값의 개수 = 하나의 키 값
    • 'KOREA'라는 조건으로 검색하면 10,000 / 10 => 대략 1000건이 일치할 것이다. 
    • 'SEOUL'인 레코드는 1건이므로 999건은 불필요하게 읽은 값이다. 
  • case B) country 칼럼의 유니크한 값의 개수가 1000개
    • 전체 레코드 건수 / 유니크 값의 개수 = 하나의 키 값
    • 'KOREA' 라는 조건으로 인덱스를 검색하면  10,000 / 1,000  = 10건이 일치한다. 
    • 10 건 중에서 'SEOUL'을 만족하는 1건이므로 9개는 불필요하게 읽은 것이다.

 

 

 

8.3.3.4 읽어야하는 레코드의 건수

 

  • 인덱스를 통해 데이터를 읽는 것은 인덱스를 거치지 않는 것보다 높은 비용이 드는 작업이다. 
  • 인덱스를 이용할지 이용하지 않을지 결정하는 방법은? 
    • 인덱스의 손익 분기점을 비교한다. 
    • 인덱스를 통해 읽어야 할 레코드의 건수(옵티마이저가 판단한 예상 건수) > (전체 테이블 레코드의 20 ~ 25%)인 경우 테이블을 모두 직접 읽어서 필요한 레코드만 가려내는 것이 효율적이다.

 

 

8.3.4 B-Tree 인덱스를 통한 데이터 읽기

 

 

8.3.4.1 인덱스 레인지 스캔(Index range scan)

  • 인덱스의 접근 방법 중 가장 대표적이다.
  • 이 단원에서는 인덱스를 통해 레코드를 한 건만 읽는 경우와 한 건 이상을 읽는 경우를 묶어서 인덱스 레인지 스캔이라고 한다. 
  • 스캔해야할 인덱스 범위가 결정 됐을 때 사용하는 방식이다.
  • 검색 값의 수나 검색 결과 레코드 건수와 상관없이 레인지 스캔이라고 표현한다.
  • 아래 그림과 같이 루트 노드부터 시작해서 브랜치 노드를 거치고 최종적으로 리프 노트까지 들어가야만 필요한 레코드의 시작 지점을 찾을 수 있다. 
  • 스캔 하다가 리프 노의 끝까지 읽으면 리프 노드 간의 링크를 이용해서 다음 리프 노드를 찾아서 다시 스캔한다. 
  • 스캔을 멈춰야할 위치에 다다르면 지금껏 읽은 레코드를 사용자에게 반환하고 쿼리를 끝낸다.

 

 

  인덱스 레인지 스캔 과정

  1. 인덱스에서 조건을 만족하는 값이 저장된 위치를 찾는다. (인덱스 탐색)
  2. 1번에서 탐색된 위치부터 필요한 만큼 인덱스를 차례대로 쭉 읽는다. (인덱스 스캔)
  3. 2번에서 읽어 들인 인덱스 키와 레코드 주소를 이용해서 레코드가 저장된 페이지를 가져오고 최종 레코드를 읽어온다. (커버링 인덱스)

 

실제 인덱스만을 읽는 경우

 

  • 위 그림은 실제 인덱스만 읽는 경우를 보여준다. 
  • 아래 그림은 B-Tree 인덱스의 리프 노드를 스캔하면서 실제 데이터 파일의 레코드를 읽어와야하는 경우를 보여준다.
  • 루트와 브랜치 노드를 이용해서 스캔 시작 위치를 검색하고 
  • 필요한 방향으로 인덱스를 읽어 나간다. 
  • 어느 방식으로 스캔하든 상관없이 인덱스를 구성하는 칼럼의 순서 또는 역순으로 정렬된 상태의 레코드를 가져온다.
    • 인덱스 자체의 정렬 특성 때문에 자동으로 그렇게 된다.
  • 리프 노드에서 검색조건에 일치하는 것들은 데이터 파일에서 레코드를 읽어오는 과정이 필요한다.
  • 리프 노드에 저장된 레코드 주소로 데이터 파일의 레코드를 읽어오는데 레코드 한 건 단위로 랜던 I/O가 한 번씩 일어난다.
  • 즉, 인덱스를 통해 데이터 읽는 방법은 비용이 많이 든다.

 

 

 

 

8.3.4.2 인덱스 풀 스캔

  • Def) 인덱스 풀 스캔: 인덱스 리프 노드의 제일 앞, 또는 제일 뒤로 이동한 후, 인덱스의 리프 노드를 연결하는 링크드 리스트를 따라서 처음부터 끝까지 스캔하는 방식을 말한다. 
  • 인덱스 레인지 스캔과 마찬가지로 인덱스를 사용하지만 인덱스의 처음부터 끝까지 모두 읽는 방식을 인덱스 풀 스캔이라고 한다.
  • 쿼리의 조건절에 사용된 칼럼이 인덱스의 첫 번째 칼럼이 아닌 경우에 인덱스 풀 스캔을 사용한다.
  • 인덱스 뿐만 아니라 데이터 레코드까지 읽어야하는 경우는 이 방법을 쓰지 않는다.
  • 인덱스 레인지 스캔보다 빠르지는 않지만 테이블 풀 스캔 보다는 효율적이다.

 

 

8.3.4.3 루스 인덱스 스캔(Loose Index Scan)

  • Def) 루스 인덱스 스캔: 느슨하게 인덱스를 읽는 것을 의미한다.
  • 인덱스 레인지 스캔과 비슷하게 작동하지만 중간에 필요하지 않은 인덱스 키 값은 무시하고 다음으로 넘어가는 형태이다. 
  • GROUP BY, MAX(), MIN() 함수에 대해 최적화를 하는 경우에 사용된다.
  • Oracle 의 "인덱스 스킵 스캔"이라는 기능과 작동 방식이 비슷한다.
  • 인덱스 레인지 스캔, 인덱스 풀 스캔은 "타이트 인덱스 스캔"으로 분류한다.

 

 

8.3.4.4 인덱스 스킵 스캔

  • 데이터베이스에서 인덱스의 핵심은 값이 정렬되어 있다는 것이고 이로 인해 인덱스를 구성하는 칼럼의 순서가 중요하다.

 

  • ex)
  • 다음과 같이 인덱스를 생성하고나서 사용하려면  WHERE 절에 gender, birth_date 에 대한 비교 조건이 필수적이다.
  • 둘 중에 하나만 where 절에 넣으면 인덱스를 효율적으로 사용할 수 없다. 
ALTER TABLE employees
ADD INDEX ix_gender_birthdate (gender, birth_date);

 

  • Def) 인덱스 스킵 스캔: 여러 개의 칼럼으로 인덱스를 생성한 경우, 옵티마이저가 하나의 칼럼만으로도 인덱스 검색이 가능하도록 해주는 기능이다. MySQL 8.0 부터 도입됐다.
    •  MySQL 8.0 이전 버전에서 비슷한 기능인 "루스 인덱스 스캔"이 있었지만 GROUP BY 작업을 처리하기 위해 인덱스를 사용하는 경우에만 적용 가능했다. 
    •  인덱스 스킵 스캔은  WHERE 조건절의 검색을 위해 사용 가능하도록 용도가 넓어졌다.
    • SET optimizer_switch='skip_scan=on'; => 활성화
  • SQL 쿼리의 맨 앞에 "EXPLAIN"을 붙이면 id, table, type, key, extra 가 표와 같은 형태로 출력된다.
    • type = index: 인덱스를 처음부터 끝까지 모두 읽었다는 뜻 (풀 인덱스 스캔) - 비효율적
    • type = range:  인덱스에서 필요한 부분만 읽었다는 뜻이다.
    • extra = Using index for skip scan : 인덱스 스킵 스캔을 활용해서 데이터를 조회했다는 것을 의미한다.

 

 

 

인덱스 스킵 스캔의 단점

  • WHERE  조건전에 조건이 없는 인덱스의 선행 칼럼의 유니크한 값의 개수가 적어야 한다.
    • 쿼리 실행 계획의 비용 관련 부분
    • 유니크한 값이 많으면 인덱스 스캔 시작 지점을 검색하는 작업이 많아질 것이다. 따라서 처리 성능 느려질 수 있다.
    • 따라서, 인덱스의 선행 칼럼이 가진 유니크한 값의 개수가 적을 때만 인덱스 스킵 스캔을 적용 가능하다.
  • 쿼리가 인덱스에 존재하는 칼럼만으로 처리가 가능해야 한다. (커버링 인덱스)

 

 

 

8.3.5 다중 칼럼(Multu-column) 인덱스

  • 실제 서비스용 데이터베이스에서는 2개 이상의 칼럼을 포함하는 인덱스(다중 칼럼 인덱스, 복합 칼럼 인덱스, Concatenated Index)가 많이 사용된다.
  • 그림을 보면 인덱스 두 번째 칼럼은 첫 번째 칼럼에 의존해서 정렬돼 있다. 두 번째 칼럼의 정렬은 첫 번째 칼럼이 똑같은 레코드에서만 의미가 있다.
  • 만약 컬럼이 4개인 인덱스라면?
    • 세 번째 칼럼은 두 번째 칼럼에 의존해서 정렬되고 네 번째 칼럼은 세 번째 칼럼에 의존해서 정렬된다.

 

 

 

8.3.6 B-Tree 인덱스의 정렬 및 스캔 방향

  • 인덱스를 어느 방향으로 읽을지는 쿼리에 따라 옵티마이저가 실시간으로 만들어내는 실행 계획에 따라 결정된다.

 

 

8.3.6.1 인덱스의 정렬

  • 일반적인 상용 DBMS에서는 인덱스를 생성하는 시점에 인덱스를 구성하는 각 컬럼의 정렬을 오름차순 또는 내림차순으로 설정할 수 있다. 

 

 

8.3.6.1.1 인덱스 스캔 방향

  • 인덱스는 항상 오름차순으로 정렬되어 있지만 오름차순, 내림차순으로 값을 가져올 수 있다. 
  • 오름차순 인덱스(Ascending index): 작은 값의 인덱스 키가 B-Tree 의 왼쪽으로 정렬된 인덱스
  • 내림차순 인덱스(Descending index): 큰 값의 인덱 키가 B-Tree 의 왼쪽으로 정렬된 인덱스
  • 인덱스 정순 스캔(Forward Index scan): 인덱스 키의 크고 작음에 관계없이 인덱스 리프 노트의 왼쪽 페이지부터 오른쪽으로 스캔한다. 
  • 인덱스 역순 스캔(Backword index scan): 인덱스 키의 크고 작음에 관계없이 인덱스 리프 노드의 오른쪽 페이지부터 왼쪽으로 스캔한다.
  • InnoDB에서 인덱스 역순 스캔이 인덱스 정순 스캔에 비해 느릴 수 밖에 없다.
    • 이유 1) 페이지 잠금인덱스 정순 스캔에 적합한 구조이다.
    • 이유 2) 페이지 내에서 인덱스 레코드단방향으로 연결된 구조이다.

 

이미지 출처 - https://tech.kakao.com/2018/06/19/mysql-ascending-index-vs-descending-index/

 

 

 

8.3.7 B-Tree 인덱스의 가용성과 효율성

  • 어떤 조건에서 인덱스를 사용할 수 있고 어떨 때 사용할 수 없는지 살펴 보자. 

 

 

8.3.7.1 비교 조건의 종류와 효율성

  • 다중 칼럼 인덱스에서 각 칼럼의 순서와 칼럼에 사용된 조건이 동등 비교(=) 인지 아니면 범위 조건(>, <)인지에 따라서 인덱스 칼럼의 활용 형태가 달라지고 효율도 달라진다. 

 

  Ex) 

SELECT *
FROM dept_emp
WHERE dept_no = 'd002' AND emp_no >= 10114;
  • case A: INDEX(dept_no, emp_no)
    •  dept_no = 'd002' AND emp_no >= 10114 를 만족하는 레코드를 찾고
    • 그 dept_no = 'd002'가 아닐 때까지 인덱스를 쭉 읽기만 하면 된다.
    • 사용자가 원하는 결과만 읽기 때문에 효율적이다.
    • dept_no, emp_no: 둘다 작업 범위 결정 조건이다. 
  • case B: INDEX(emp_no, dept_no) 
    • emp_no >= 10114  AND  dept_no = 'd002' 인 레코드를 찾고 
    • 그 이후 모든 레코드에 대해 dept_no = 'd002'  조건을 만족(필터링)하는 레코드를 찾는다. 
    • 필터링: 인덱스를 통해 읽은 레코드가 나머지 조건에 맞는지 비교하면서 취사 선택하는 작업을 말한다.
    • 아래 그림을 보면 5건의 레코드를 찾기 위해 7번의 비교 과정을 거친 것이다. 
    • emp_no: 작업 범위 결정 조건, dept_no: 필터링 조건 

 

 

 

 

8.3.7.2 인덱스의 가용성

 

  • B-Tree 인덱스의 특징은 왼쪽 값을 기준으로 오른쪽 값이 정렬돼있는 것이다. 
  • 왼쪽이란 하나의 칼럼 뿐만 아니라 다중 칼럼 인덱스의 칼럼에 대해서도 함께 적용된다.

 

 

SELECT *
FROM employees 
WHERE first_name LIKE '%mer';
  • 이 쿼리는 인덱스 레인지 스캔 방식으로 인덱스를 이용할 수 없다. 
    • 이유: first_name 칼럼에 저장된 값의 왼쪽부터 한 글자씩 비교하면서 일치하는 레코드를 찾아야되는데 조건절에 주어진 상숫값(%mer)에는 왼쪽 부분이 고정되지 않았기 때문이다. 
    • 따라서 정렬 우선순위가 낮은 뒷부분의 값만으로는 왼쪽 기준 정렬 기반의 인덱스인 B-Tree에서는 인덱스의 효과를 얻을 수 없다. 

 

SELECT *
FROM dept_emp 
WHERE emp_no
  • 인덱스가 (dept, emp_np) 칼럼 순서대로 생성됀 경우, 선행 칼럼인 dept_no 조건 없이 emp_no 값으로만 검색하면 인덱스를 효율적으로 사용할 수 없다. 
    • case B의 인덱스는 다중 칼럼으로 구성된 인덱스이므로 dept_no 칼럼에 대해 먼저 정렬한 다음 다시 emp_no 칼럼 값으로 정렬돼 있기 때문이다. 

 

 

 

8.3.7.3 가용성과 효율성 판단

  • 체크 조건으로 인덱스를 사용 가능
  • NOT-EQUAL로 비교된 경우 (<>, NOT IN, NOT BETWEEN, IS NOT NULL)
  • LIKE '%??'   (앞부분이 아닌 뒷부분 일치) 형태로 문자열 패턴이 비교된 경우
  • 스토어드 함수나 다른 연산자로 인덱스 칼럼이 변형된 후 비교된 경우
  • NOT-DETERMINISTIC 속성의 스토어드 함수가 비교 조건에 사용된 경우
  • 데이터 타입이 서로 다른 비교(인덱스 칼럼의 타입을 변환해야 비교가 가능한 경우)
  • 문자열 데이터 타입의 콜레이션이 다른 경우

 

 

 

출처 - 「Real MySQL 8.0 - 백은빈, 이성욱」 위키북스