<새로운 인덱스 생성의 의미>
[VLDB: 인덱스 선정을 통한 엑세스 형태의 효율화를 위한 조언 ]
\- 적은 데이터를 가진 소형 테이블
\- 주로 참조되는 역할을 하는 중대형 테이블
\- 업무의 구체적인 행위를 관리하는 중대형 테이블
\- 저장용 대형 테이블
가) 적은 데이터를 가진 소형 테이블
\- 한번의 다중 블록 I/O Call로 모든 내용을 읽을 수 있으므로, 인덱스를 통한 스캔과 거의 차이가 없음. 그럼에도 불구하고 생성해야 하는가?
\-> 인덱스 유무는 옵티마이저의 판단에 많은 영향을 미치는데 특히 조인과 인덱스는 옵티마이저의 결정에 큰 영향을 미침
인덱스는 가장 큰 영향요소 이므로 존재 여부에 따라 실행 계획에 큰 영향을 미칠 수 있음.
해당 유형의 인덱스는 Nested Loop Join시 내측루프를 주로 수행하므로 많은 횟수가 수행될 수 있음. 약간의 차이가 전체적으로 큰 영향을 미칠 수 있음.
\- 권고
작은 테이블이라도 PK는 반드시 생성을 권고하며, PK에 의해 참조가 많이 되는 테이블의 경우는 조인 시 효율을 위해 IOT 생성 검토도 필요
나) 주로 참조되는 역할을 하는 중대형 테이블
\- 의미: 트랜잭션 데이터들의 행위의 주체나 목적이 되는 개체들로 구성된 테이블들. 모델링에서 키 엔티티로 분류되는 집합임.
\- 특징: 좁은 범위의 스캔 또는 조인등에 의해 내측 루프에서 기본키에 의해 연결됨
검색 조건의 형태가 뚜렷하고, 데이터 증감이 별로 없으며, 검색 위주의 엑세스 발생
\- 권고
다) 업무의 구체적인 행위를 관리하는 중대형 테이블
\- 의미: 업무의 구체적인 내용을 담고 있는 테이블.
\- 특징: 조인에서 주로 외측 루프를 담당하고 있음.
테이블이 크고, 지속적으로 증가하고 있으므로 많은 인덱스는 입력 시 부하가 가게 하며, 인덱스 구성이 약간만 잘못 되어도 처리 범위가 증가할 수 있음.
다양한 엑세스 조건으로 인덱스의 수가 많고 복합 인덱스가 많으며, 다양한 연산자가 존재하여 결합 인덱스의 순서 또한 중요
\- 권고
라) 저장용 대형 테이블
\- 의미: 로그성 데이터를 관리할 목적으로 생성된 테이블
\- 특징: 대량의 데이터를 가지고 있으며, 지속적으로 대량의 데이터가 입력됨.
\- 권고
\- 인덱스 생성의 목적: 전체 집합에서 범위를 줄여 선별된 부분만 엑세스 하고자 함
\- 분포도: 전체 집합에서 해당 컬럼으로 조회 시 해당 조건이 차지하는 비율을 의미.
\- 손익 분기점: 인덱스는 단일 블록 I/O를 유발하므로 분포도가 높은 경우에는 인덱스가 존재한다 할지라도 사용하지 않을 수 있다.
손익 분기점은 이렇게 인덱스 사용 여부를 결정하는 분포도 값이다. 옵티마이저는 손익분기점 이상이면 인덱스를 사용하지 않고 이하면 사용한다.
\- 인덱스의 분포도가 높으면 무조건 인덱스를 생성하지 않는 것이 유리한가?
\--> 반드시 그렇지 않다. 부분 범위 처리를 할 경우 실제 분포도는 아주 낮아질 수 있다.
\- 인덱스 머지(Index Merge): 여러 인덱스가 협력하여 같이 엑세스를 주관
\- 결합 인덱스: 여러 컬럼을 모아 하나의 인덱스로 만드는 방식.
\- Index Merge 방식과 결합 인덱스 방식의 연산 차이
< 결합 인덱스의 특징 >
가) 분포도와 결합 순서의 상관 관계
ex) 구성
\- 테이블: TAB1, 컬럼: COL1, COL2
\- 컬럼 COL1: 분포도가 좋지 않음
\- 컬럼 COL2: 분포도가 좋음
\- 사용된 SQL
| SELECT * FROM TAB1 WHERE col1='A' AND col2='112' |
|---|
\- 엑세스 방식에 따른 구분

\* 분포도가 좋지 않은 COL1 컬럼을 복합인덱스의 선행 컬럼으로 설정한 경우 처리 과정(왼쪽 그림)
1. col1='A' AND col2=112 인 데이터를 B-Tree 방식으로 바로 찾음
2. ROWID를 이용하여 테이블 로우 엑세스
3. 다음 로우를 탐색하여 조건에 만족하면 다시 테이블의 로우를 엑세스하고 그렇지 않으면 스캔 종료(이 경우, 한 번의 랜덤, 2개의 인덱스 로우 스캔)
\* 분포도가 좋은 COL2 컬럼을 복합인덱스의 선행 컬럼으로 설정한 경우 처리 과정(오른쪽 그림)
1. col2=112 AND col1='A' 인 데이터를 B-Tree 방식으로 바로 찾음
2. ROWID를 이용하여 테이블 로우 엑세스
3. 다음 로우를 탐색하여 조건에 만족하면 다시 테이블의 로우를 엑세스하고 그렇지 않으면 스캔 종료(이 경우, 한 번의 랜덤, 2개의 인덱스 로우 스캔)
\* 결론: 인덱스의 모든 컬럼을 Equal 연산으로 처리한 경우 처리량에는 차이가 없음. 단지 Equal 연산만 사용했으므로 필요한 데이터만 읽을 수 있게 되었기 때문.
나) 이퀄(=)이 결합순서에 미치는 영향
\- 실제 실무에서는 Equal 연산만으로 처리되지 않고, 다양한 범위 처리 연산(LIKE, BETWEEN, < > 등)이 사용됨
인덱스의 선행 컬럼에 대해 Equal 연산이냐 아니냐는 처리 범위에 크게 영향을 미침
ex) 구성
\- 테이블: TAB1, 컬럼: COL1, COL2
\- 컬럼 COL1: 분포도가 좋지 않음
\- 컬럼 COL2: 분포도가 아주 좋음
\- 사용된 SQL
| SELECT * FROM TAB1 WHERE col1='A' AND col2 between '113' and '115' |
|---|
\- 엑세스 방식에 따른 구분

\* 분포도가 좋지 않은 COL1 컬럼을 복합인덱스의 선행 컬럼으로 설정한 경우 처리 과정(왼쪽 그림)
1. col1='A' AND col2=113 인 첫번째 로우를 찾는다.
2. ROWID를 이용하여 테이블의 로우를 엑세스 한다.
3. 다음 로우를 차례로 스캔하면서 col1 \!='A' OR col2 > 115 일 때 까지 테이블의 로우를 엑세스 하고 그렇지 않으면 처리를 종료한다.
◎ 이 경우, 첫 번째 컬럼이 Equal 연산이고 두 번째 컬럼에는 정렬이 되어 있으므로 between이 들어갔다 하더라도 필요한 엑세스만 하게 된다.
\* 분포도가 좋은 COL2 컬럼을 복합인덱스의 선행 컬럼으로 설정한 경우 처리 과정(오른쪽 그림)
1. col2=113 AND col1='A' 인 첫번째 로우를 찾는다.
2. ROWID를 이용하여 테이블의 로우를 엑세스 한다.
3. col2 > 115 일 때 까지 계속해서 스캔한다. 스캔한 로우는 col1='A' 인지 체크하여 성공하면 ROWID를 이용하여 테이블을 엑세스한다.
4. col2 > 115 이면 처리를 종료한다.
◎ 이 경우, col2 에 대해서는 체크만 하는 이유는 이 컬럼에 대해서는 정렬이 되어있지 않기 때문. 체크로 인해 ROWID를 이용한 테이블 엑세스만 줄어들 뿐이다.
아래의 경우에서 col1 컬럼에 대해서만 인덱스가 있는 경우에서 차이를 볼 수 있다.
11번의 ROWID 스캔에서 3번의 ROWID 스캔으로만 줄고, 실제 인덱스 내부 처리 범위내에서는 효율을 보지 못했다. 
\- 결론: 인덱스의 처리 범위를 고려해야 하며 컬럼의 분포도를 고려한 인덱스 컬럼 선정 방식은 맞지 않다.
조회 시 사용되는 컬럼에 Equal를 많이 쓰는 경우 처리 범위를 크게 줄여주므로 해당 컬럼을 인덱스 선행 컬럼으로 두는 것이 올바르다.
다) IN 연산자를 이용한 징검다리 효과
\- 적용 예
1. 비효율적인 인덱스가 이미 큰 테이블에 대해 사용하고 있는 경우
( 위 예에서 col2, col1 순으로 생성된 결합 인덱스)
2. 올바르게 컬럼 순서로 생성된 인덱스의 쿼리 유형에 상반되는 컬럼 순서의 쿼리가 가끔 사용되는 경우
( col1(=), col2(between) 순서로 들어오는 쿼리가 대부분이고 인덱스고 그 순서로 생성되어있는데, 가끔 col2(=), col1(between) 과 같은 쿼리도 수행되는 경우)
◎ 이 경우, 새롭게 인덱스를 생성하는 것은 인덱스의 중복 투자이므로 올바르지 않음. IN 연산자를 통해 기존 인덱스 만으로도 효율적인 결과를 가져올 수 있음
\- 사용 예제
\* 기존의 between 연산을 이용한 경우(왼쪽 그림)
col2 컬럼 기준으로 넒은 범위 스캔을 한다. col1 컬럼의 필요한 컬럼인 'A'만을 뽑아내지 못한다. 이 경우, 범위에 해당되는 모든 인덱스를 읽으므로 선을 그은것과 같다 하여 '선(line)연산' 이라고도 한다.
\* IN 연산을 이용한 경우(오른쪽 그림)
INLIST ITERATOR(111,112)
1. col2=:value AND col1='A' 인 첫번째 로우를 찾는다. 이 때, 인덱스 트리를 탐색하는 과정이 포함된다.
2. ROWID 탐색을 하고 테이블의 데이터를 뽑아낸다.
3. col2\!=:value OR col1\!='A' 일 때 까지 테이블의 로우를 엑세스하고 만족하지 않으면 다음의 Iterator 값을 뽑아내 1번 과정을 반복한다.
만약 값이 없으면 전체적인 탐색을 종료한다.
◎ 위 연산은 IN이 가지는 연산의 특징으로 col2 , col1 컬럼의 equal 연산을 여러번 수행하여 불필요한 인덱스 값을 읽는 것을 방지한다.
IN 리스트 내부에 있는 데이터들로 인해 필요한 인덱스 값만 읽게 되는데, 해당 인덱스에 점을 찍은것과 같다하여 '점(point)연산' 이라고도 한다.
\* 위 IN 리스트를 사용한 쿼리는 다음의 OR를 사용한 쿼리와 거의 동일한 수행방식을 가진다.
| SELECT * FROM TAB1 WHERE (COL2=111 AND COL1='A') OR (COL2=112 AND COL1='A') |
|---|
라) 처리 범위에 직접적인 영향을 주지 못하는 컬럼의 추가 기준
\- 결합 인덱스를 사용하는 쿼리 내부에 equal 연산을 사용하지 않는 컬럼 이후로는 범위를 줄여주지 못함.
(예로써, (A,B,C,D)로 구성된 인덱스에 A(=),B(between),C(=),D(=) 와 같이 사용했을지라도 B 연산에서 범위 연산자를 쓰게 됨에 따라 C,D 컬럼은 인덱스 스캔 범위를 줄여주지 못함)
ex) 테이블 구성
\- col1: equal(=) 연산이 사용됨.
\- col2: 사용되지 않거나, 범위 연산자(like, between, < , > 등)이 사용됨.
\- col3: 다양한 처리 연산이 사용됨
\- 사용 쿼리
| SELECT * FROM TAB1 WHERE A='2' AND C='61' |
|---|
\- 사용 예제
\* (A+B) 컬럼에 복합인덱스를 만든 경우(왼쪽 그림)
1. A 컬럼에 대해서는 인덱스가 존재하므로 A='2' 인 모든 노드를 탐색한다.
2. 그러나 C 컬럼에는 인덱스가 없으므로 ROWID를 통한 테이블 억세스으로 테이블 데이터에서 C 컬럼을 뽑아내 C='61' 것을 필터링 한다.
3. 테이블 내부에서 필터링해서 조건에 맞는 것만 출력한다. 이 과정은 인덱스 스캔이 종료될때 까지 계속 수행된다.
◎ 인덱스 탐색 결과 나온 5건에 대해 모두 ROWID를 통한 테이블 엑세스를 한다. 위 경우 총 5번의 테이블 스캔을 한다.
\* (A+B+C) 컬럼에 복합인덱스를 만든 경우(오른쪽 그림)
1. A 컬럼에 대해서는 인덱스가 존재하므로 A='2' 인 모든 노드를 탐색한다.
2. 앞선 B 컬럼의 검색 조건 부재로 C 컬럼이 인덱스 검색 범위를 줄여주진 못하나, 값은 존재하므로 C='61' 인 것을 필터하는 부분을 인덱스 내부에서 해결한다.
3. 인덱스 내부에서 필터링해서 조건에 맞는 것만 ROWID를 통한 테이블 억세스로 값을 뿌려준다.
◎ 인덱스 탐색 결과 나온 5건에 대해 필터를 통해 걸리진 1건에 대해서만 ROWID를 통한 테이블 스캔을 한다. 위 경우 총 1번의 테이블 스캔을 한다.
< 컬럼 순서를 결정하는 우선 순위 >
[VLDB: 상황 가정 ] : C1, C2, C3, C4 컬럼들에 대해 복합인덱스 컬럼 순서를 어떻게 결정할 것인가?
\- 아래는 쿼리가 사용하고 있는 쿼리 조건의 목록임
1. C1(=), C2(BETWEEN), C3(=)
2. C2(=), C3(BETWEEN), C4(>)
3. C1(=), C2(=), C3(LIKE)
4. C1(=), C2(=), C4(>)
▶ 1단계: 항상 사용하는가?
\* 컬럼의 사용 횟수: C2(4회), C1(3회), C3(2회), C4(2회)
\- 추가 가정: C1 컬럼의 카디널러티가 낮아서 해당 컬럼이 없는 경우에 대해서는 Index Skip Scan 사용하도록 가정
두 번째 경우에서 C2의 경우에서 Equal 연산이 사용되므로 C1 케이스에서 \+1 해서 총 4회 사용으로 가정
▶ 2단계: 항상 '='로 사용하는가?
▶ 3단계: 어느 것이 더 좋은 분포도를 가지는가?
\* 분포도를 고려 및 사용 가능한 추가 연산 형태(예로써 Between을 IN으로 변환 가능한 경우) C1, C2 컬럼의 순서를 추가 검토
\* 이후에 발생할 상황 추이 후 검토
▶ 4단계: 자주 정렬되는 순서는 무엇인가?
\* 정렬을 요구하는 구문의 사용 시, 정렬 순서 조사 후 검토. 정렬 시 앞단에 나오는 컬럼을 우선적으로 두는 것이 맞다.
▶ 5단계: 부가적으로 추가시킬 컬럼은 어떤 것으로 할 것인가?
가) 테이블의 엑세스 형태를 최대한 수집
▶ 개발 단계에서의 엑세스 형태 수집
1) 반복 수행되는 엑세스 형태를 찾는다. 반복 수행되는 엑세스는 자신의 수행속도에 반복횟수를 곱한 만큼의 부하를 가져오므로 수행속도에 미치는 영향이 아주 크다.
▶ 운영 단계에서의 엑세스 형태 수집
▶ 수집된 SQL을 테이블 별로 출력하여 엑세스 형태 기록
나) 인덱스 대상 컬럼의 선정 및 분포도 조사
1) 인덱스 대상 컬럼 선정 및 분포도 분석
2) 현행 인덱스 존재 시, 조사한 엑세스 형태가 어떤 인덱스를 타는지 조사
다) 특수한 엑세스 형태에 대한 인덱스 선정
\- 반복해서 엑세스 되는 형태(Critical Access Path)를 찾아내는 것이 우선으로 해야할일임
라) 클러스터링 검토
\- 넓은 범위 처리를 자주하는 컬럼에 대해 클러스터링 검토
마) 결합 인덱스 구성 및 순서의 결정
\- 앞서 설명한 결합 인덱스 순서 구성 원리에 따라 선정
바) 시험 생성 및 테스트
사) 수정이 필요한 애플리케이션 조사 및 선정