> For the complete documentation index, see [llms.txt](https://real-dev.gitbook.io/real-library/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://real-dev.gitbook.io/real-library/sql-tuning/readme/2.-1.md).

# 2.인덱스 기본

#### 핵심

1. 인덱스는 큰 테이블에서 소량 데이터를 검색할 때 사용한다.
2. **인덱스 스캔 과정**에서 발생하는 비효율을 줄인다.
3. **테이블 액세스 횟수**를 줄인다. (대부분의 DB 튜닝은 랜덤 액세스를 줄이기 위해 개발되었다.)

<br>

![Untitled](/files/uHwOJHhf72ylFRf9yrTt)

인덱스 구조 및 탐색 예시 (고객명 기준 인덱스에서 ‘이재희’를 찾는 과정)

<br>

이중 LMC는 키 값이 없는 가상의 레코드를 의미하며, 이 레코드가 가리키는 자식 노드는 키 값을 같는 가장 작은 레코드보다 더 작은 데이터들이 저장되어 있다. 일종의 0번 출발지인셈

<br>

리프 블록에 저장된 레코드는 키 값으로 정렬되어 있고, 키 값이 동일하다면 ROWID를 기준으로 정렬되어 있다. 인덱스를 사용하는 이유는 결국 이 ROWID를 빠르게 얻어서 테이블에 접근하기 위함.

<br>

#### 인덱스 수직 탐색

* 정렬된 인덱스 레코드 중 조건을 만족하는 첫 번째 레코드(인덱스 스캔 시작 지점)를 찾는 과정.
* 수직 탐색 과정은 찾고자 하는 값보다 크거나 같은 값을 만나면 바로 직전 레코드가 지목하는 하위 블록으로 이동한다.

<br>

#### 인덱스 수평 탐색

* 수직적 탐색을 통해 조건을 만족하는 첫 번째 레코드를 찾았다면, 거기서부터 조건을 만족하지 않는 레코드가 나올 때까지 탐색하는 과정
* 이게 가능한 이유는 인덱스 리프 노드가 앞뒤 노드를 연결하는 Double Linked List로 구현되었기 때문
* 또한, 인덱스가 조회에 필요한 모든 데이터를 가지고 있어, 테이블에 액세스하지 않는 경우도 있지만, 일반적으론 인덱스 조회 이후 ROWID를 가지고 테이블에도 액세스하기 때문

<br>

#### 결합 인덱스

![Untitled](/files/E7V8GZoKKVVz703SC1FK)

이 때 인덱스 선두 컬럼의 순서가 변경되더라도, 해당 쿼리에선 동일하게 작동한다.

결합 인덱스와 관련해서 잘못 알려진 사실 중 하나를 저자는 강조하는데, `SELECT * FROM 고객 WHERE 성별 = ‘남’ and 고객명 = ’이재희’` 쿼리를 통해 결합 인덱스 탐색 시, 성별 조건으로 먼저 탐색 후, 고객명으로 검색을 진행하기에 이 경우 비효율이 발생한다. 같은 논지의 설명이 퍼져있으며 아주 열받는듯하다.

<br>

![Untitled](/files/esUohavgJY4SZsrilRRW)

진짜 있을 줄이야…

<br>

결합 인덱스 탐색 시에, 인덱스 선두 컬럼과 조회 조건문이 모두 = 조건으로 비교된다면, 이로 인한 차이는 발생하지 않는다. **인덱스 구성에 있어 컬럼 순서가 매우 중요한 영향을 끼치는 건 맞지만, 이런 식으로 작동하지는 않는다는 것.**

<br>

다만 동일한 키워드로 검색했을 때, 본 서적의 내용이 좀 더 많이 노출되는 경향성을 보인다. 저자의 노력은 성공적으로 빛을 발하고 있는듯하다.

<br>

#### 인덱스 기본 사용법

**제 1원칙. 인덱스 컬럼을 가공하면 인덱스를 정상적으로 스캔할 수 없다.**

쉽게 말해 제목 컬럼 같은거 인덱스로 만들고 여따가 Like %#{title}% 이런식으로 검색하더라도 인덱스는 작동할 수 없다는 것. Like #{title}% 이건 된다. 인덱스가 어떤 식으로 만들어진건지 형태를 알면 이해가 어렵지 않은 부분. Index Range 탐색이 불가능해지면 성능은 많이 떨어진다.

![Untitled](/files/Zudrj5O7TMdBGTESfXJX)

Index Range Scan이 어려운 경우. 다만, OR이나 IN은 옵티마이저에 의해 가능하도록 변경되기도 한다.

**제 2원칙. 인덱스 선두 컬럼이 조건절에 들어있어야 Index Range Scan이 가능하다.**

다만, Index Range Scan이 일어난다고해서 무조건 좋은 쿼리는 아니다. 인덱스를 통해 스캔 범위 자체를 유의미하게 줄일 수 있어야한다. 쿼리로만 해결되지 않는다면 파티셔닝이나 인덱스 튜닝 같은 추가 작업을 겸해야 할 수 있다.

추가로 인덱스를 통해 정렬을 생략해서 성능 향상을 노릴 수 있지만, 이 컬럼에도 변형이 가해지면 정렬이 불가할 수 있다.

<br>

**제3 원칙. 자동 형변환을 주의해야한다. 자동 형변환에 의한 인덱스 컬럼 변환으로 인해 Index Range Scan이 불가능해질 수 있다.**

자동 형변환은 언뜻 편리하고, TO\_CHAR()같은 형변환 함수가 없으면 성능적인 이점이 있을 것 같지만 그렇지가 않다. 인덱스 스캔에 문제가 생기거나, 프로덕트 품질에 악영향이 있을 수 있다. 무엇보다 TO\_CHAR같은 형변환 함수를 적게 사용하는게 중요한게 아니라, Block IO 자체를 줄이는 것이 SQL 성능의 핵심이다.

<br>

#### 인덱스 스캔 종류

1. Index Range Scan
   1. 가장 기본적인 인덱스 스캔. 수직 탐색과 수평 탐색을 통해 특정 블록을 빠르게 찾아낸다.
2. Index Full Scan
   1. 인덱스 수직 탐색 없이 인덱스 리프 블록을 처음부터 끝까지 탐색하는 방식이다. 인덱스 선두 컬럼에 = 조건이 없어, Index Range Scan이 불가능하고, Full Table Scan을 하기엔 테이블의 크기가 너무 크다면, 차차선책으로 선택할 수 있다. 다만, 수행빈도가 높은 쿼리라면 인덱스 튜닝을 통해 Index Range Scan으로 작동하도록 변경해주는 것이 좋다.
3. Index Unique Scan
   1. 유니크 인덱스 기준으로 = 조건으로 탐색 시, 수평 탐색 없이 수직 탐색으로만 스캔할 수 있다. 어차피 하나니까 수평 탐색할 필요가 없지. 다만, ≥ 이런식으로 범위 검색하면 당연하게도 작동할 수 없다.
4. Index Skip Scan
5. Index Fast Full Scan
   1. Index Full Scan보다 빠른 방식. 인덱스 세그먼트 자체를 Multi Block IO 방식으로 읽어버린다. 다만 결과 집합의 정렬은 보장하지 않는다.

      ![Untitled](/files/aIMCM5aR5wdWbPk6xI9F)
6. Index Range Scan Desceding

   1. Index Range Scan이 내림차순으로 동작한다.

   <br>
