인덱스란?
데이터베이스의 검색속도 향상을 위해 유지, 관리하는 자료구조
컬럼의 값과 해당 레코드가 저장된 주소를 키와 값의 쌍으로 삼아 인덱스를 만든다.
Ex) 책의 색인, 위치를 가리키는 지표
인덱스의 사용 목적
조건을 만족하는 튜플을 빠르게 조회하기 위해
사용하는 쿼리에 맞춰서 적절하게 인덱스를 걸어줘야 쿼리가 빠르게 처리될 수 있다.
인덱스 특징
인덱스는 저장되는 컬럼의 값을 이용해 항상 정렬된 상태를 유지한다.
primary key는 인덱스를 자동으로 걸어준다.
Where, join에 자주 사용되는 속성을 사용한다.
랜덤 IO
데이터를 임의의 순서로 접근하는 방식
Index Range Scan 방식 (인덱스를 사용하여 특정 범위의 데이터를 검색하는 방식)
하드디스크에서 데이터를 읽을 때, 랜덤 I/O는 데이터의 특정 부분만 읽거나 쓰기 위해 디스크 헤드를 움직인다.
순차 IO
데이터를 연속적인 순서로 접근하는 방식
Full Table Scan 방식 (테이블의 모든 행을 순차적으로 읽어 조건에 맞는 데이터를 찾는 방식)
하드디스크에서 데이터를 읽을 때, 데이터의 처음부터 끝까지 읽거나 쓰기 위해 디스크 헤드를 한 방향으로 움직인다.
테이블의 모든 데이터를 조회하는 상황
대량의 데이터를 정렬하거나 그룹화 하는 상황
성능 측면
Ex) 디스크에서 읽을 위치를 찾기 위해 순차 I/O는 디스크의 헤드를 1번 움직이고, 랜덤 I/O는 디스크 헤드를 3번 움직인다.
이진탐색트리
B-tree 특징
시간복잡도 : O(logN)
CPU : 프로그램 코드가 실제로 실행되는 곳
Main memory(RAM): 실행 중인 프로그램의 코드들과 실행에 필요한 또는 그 결과로 나온 데이터들이 상주하는 곳
Secondary storage(SSD or HDD) : 프로그램과 데이터가 영구적으로 저장되는 곳, 실행 중인 프로그램의 데이터 일부가 임시 저장되는 곳 / database도 여기 저장된다.
SSD 특징
1. 데이터 처리 속도가 가장 느리다 -> DB에서 데이터를 조회할 때 secondary storage에 최대한 적게 접근하는 것이 성능 측면에서 좋다.
2. 데이터를 저장하는 용량이 가장 크다.
3. block단위로 데이터를 읽고 쓴다. = 필요하지 않은 데이터까지 가져올 수 있다.
-> 연관된 데이터를 모아서 저장하면 더 효율적으로 불러올 수 있다.
- 저장공간 활용도
- 디스크에 적게 접근(storage) : 자녀 노드 수 차이로 데이터 찾을 때 탐색범위를 빠르게 좁힐 수 있다.
단점
테이블의 레코드를 PK기준으로 비슷한 것끼리 묶어서 저장하는 형태
장점
단점
<어떤 경우에 생성할까?>
데이터의 행에 독립적이며, 인덱스 키 값과 데이터 행을 가리키는 포인터가 존재
테이블 데이터와 함께 테이블에 저장되는 것이 아니라 별도의 페이지에 인덱스를 구성한다. 한 테이블에 여러 개 저장 가능
인덱스의 리프 페이지는 데이터가 위치하는 포인터다.
장점
데이터 입력, 수정, 삭제는 더 빠르다.
단점
리프 페이지 이후 다시 데이터 페이지를 찾아가야 하기 때문에 검색 속도가 느리다. Index를 저장할 추가적인 저장공간 필요
<어떤 경우에 생성할까?>
목적
데이터를 변환하거나 특정 연산을 수행한 결과도 인덱스에 탈 수 있다
filtered index
인덱스를 생성할 때 조건을 지정하여 해당 조건을 충족하는 특정 데이터만 인덱스에 포함시킨다.
CREATE INDEX idx_active_users ON users (username) WHERE is_active = 1;
Filtered index 사용 사례
NULL 값이 대부분인 열에 인덱스를 생성할 때
필요 없는 NULL 값으로 인해서 인덱스의 크기는 쓸데 없이 커지게 된다. 이때, NULL 값을 제외한 인덱스를 생성하면 인덱스도 작아지고 검색 속도도 더욱 빨라진다.
데이터가 있더라도 특정 범위로만 검색할 경우
전 국민의 데이터가 저장되어 있다고 가정할 때, 청년 복지와 관련된 업무를 주로 하게 된다면 주로 청년의 나이 범위(30세 이하)로 검색하게 될 것이므로 해당하는 범위로만 인덱스를 생성하는 것이 좋다.
FBI
함수기반 인덱스
CREATE INDEX from_loc_idx ON orders (SUBSTR(ship_id, 5, 3));
CREATE INDEX deptno_idx ON emp (TO_NUMBER(deptno));
Covered index
쿼리에서 쓰이는 모든 컬럼을 갖고 있는 인덱스
장점: 실제 값을 안 찾아가도 돼서 조회성능이 더 빠르다
단점: 인덱스 크기가 커진다.
Include index
이 방식은 a, b 컬럼을 인덱스 키로 포함하고, c 컬럼을 인덱스에 포함하지만 키로 사용하지 않는다. INCLUDE 절에 포함된 컬럼은 인덱스의 리프 노드에만 저장된다.
장점 : 주로 조회 시 활용되는 선택적인 컬럼을 인덱스에 포함시킨다. 인덱스 크기를 증가시키지 않으면서도 쿼리의 성능을 향상시킬 수 있다. 비클러스터형 인덱스의 리프 페이지에 데이터를 포함하고 있어 쿼리 성능을 높인다.
단점 : 인덱스 크기가 커진다.
인덱스 컬럼의 데이터를 Bit 값인 0 또는 1로 변환하여 인덱스 키로
사용하는 방법
목적
장점 :
저비용 인덱스 : 비트맵으로 인덱스 값을 관리하고 해당 비트맵을 압축하므로 B-TREE 인덱스보다 작은 용량을 차지한다.
집합 연산 가능 : 여러 조건을 만족하는 행을 빠르게 찾을 수 있다.
단점 :
컬럼의 값을 쪼개서 인덱싱에 사용하므로 쪼갠 컬럼값이 포함된 데이터를 찾을 수 있다.
-> 컬럼 포함 여부를 파악하기에 적합하다. Ex) %like%
목적
Like와 같은 조건도 인덱스 탈 수 있다.
Full text scan 속도를 높이는 데 사용 가능하다. *텍스트 검색 시 선호
-> GIN 인덱스는 내부적으로 비트맵 인덱싱을 사용한다.
이는 각 단어에 대해 비트맵을 생성하여 해당 단어가 존재하는지 여부를 나타낸다. 이 비트맵을 사용하여 특정 단어가 포함된 문서를 빠르게 식별할 수 있다. 이는 전문 텍스트 검색에서 특히 유용하며, 전체 텍스트를 스캔하는 대신 비트맵을 검색하여 필요한 문서를 식별할 수 있다.
작동방식
컬럼 값들을 일정한 규칙에 따라서 쪼갠다
쪼갠 값들 btree 구조로 저장한다. (물리적 위치도 함께 저장)
검색 시 키워드가 포함된 데이터를 찾는다. Ex) 쿼리에서 검색에 사용할 lexeme인 'many'와 'slitter'를 추출한다.
B-tree에서 2개의 키를 동시에 찾는다.
마지막으로, 발견된 TID각각에 대해 특정 키 값을 포함하는 튜플을 빠르게 찾는다.
단점 :
cardinality(카디널리티)
unique한 값의 개수
유니크한 값이 많을수록 불필요한 데이터를 읽지 않게 된다.
selectivity(선택도)
= cardinality/total number of records
전체 레코드 중에서 조건절에 의해 선택될 것으로 예상되는 레코드의 비율(%)
중복도가 낮으면 선택도도 낮아진다.
→ 조건절에서 걸리는 행이 적기 때문에 탐색할 범위가 줄어든다.
→ 선택도가 높은 컬럼에 인덱스 생성하면 테이블 액세스가 많이 발생한다.
→ 카디널리티가 크고, 선택도가 낮은 컬럼으로 인덱스 생성하는 것이 효율적이다.
점조건
IN, = 연산자를 이용한 조건, 해당 연산자는 하나의 점 만을 의미한다.
선분조건
Between, like, <,> 등 조건을 만족하는 모든 실수를 의미한다.
인덱스 선두 컬럼을 모두 “=“ 조건으로 검색할 때는 어느 컬럼을 인덱스 앞쪽에 두든 블록 I/O 개수는 변하지 않아 인덱스 스캔 효율에 전혀 차이가 없다.
인덱스 컬럼을 가공하면 인덱스를 정상적으로 사용(range scan)할 수 없다.
ex) substr, %like
인덱스에는 가공되지 않은 값이 저장되어 있는데, 가공된 값을 기준으로 검색하려면 스캔 시작점, 끝지점을 찾을 수 없다. 결국 리프 블록 전체를 스캔하는 Index Full Scan하게 된다.
전체 데이터 중 추출 데이터 비율이 높으면 인덱스보다 순차 탐색의 속도가 더 빠를 수 있다.
인덱스 선두 컬럼이 조건절에 있어야 한다. 단, 실행계획을 보고 인덱스 스캔범위, 메모리 접근 횟수 등을 확인한다.
인덱스에 대해 알게 되었네요~ 고맙습니다😜