Index 알고 쓰자
BTree 기준
Index
추가적인 저장공간(테이블 크기의 10% 정도)을 활용하여 더 빠르게 조회할 수 있는 방법을 만든다.
Clustered Index
- 별도로 설정할 수 있는 값이 아닌 Mysql 설정
- 효율을 위해 중복이 최대한 발생하지 않은 칼럼 지정
- 우선 순위
- Auto Increments
PK
Column Unique
ColumnHidden Clustered Index
Key를 만들어 사용
- Auto Increments
NonClustered Index
- 우리가 흔히 아는 Table 생성 시 Index를 설정
- Table 당 Index는
최대 65개의 Index
를 가지고 있다. ⇒ 64개의 NonCluster Index가 설정 가능 + Cluster Index(1개) - Multi Column Index는
최대 16개
의 칼럼이 사용
1
2
3
4
5
6
7
8
-- CREATE INDEX [인덱스명] ON [테이블명](컬럼1, 컬럼2, 컬럼3.......)
CREATE INDEX INDEX_NAME ON table(col1,col2);
-- 컬럼 중복 X
CREATE[UNIQUE] INDEX INDEX_NAME ON table(col1,col2);
-- DROP INDEX [인덱스 명]
DROP INDEX INDEX_NAME;
Index로 사용해야 할 칼럼 종류
- index
key
크기 - b-tree의
level
- selectivity =
Cardinality
와 골고루 퍼져있는지 read raw 수
- 100만개를 다 읽어야한다면? index가 필요 없다.
- 전체 record 대비 20% 미만의 record를 read하는 경우 효율적
- 전체 데이터의 5~10정도로 걸러져야 효율이 좋다고 한다.
Page
- Mysql Index = BTree 사용
- Page =
16KB
- index = (key,value)로 value는 자식 노드의 주소 값이기에 고정이고, key의 크기에 따라 하나의 page에 들어갈 수 있는 index수가 결정된다. ( index key의 크기가 크다면 갯수가 줄어들어 비효율적 )
-
Tree 구조에서 최상단을
Root
, 최하단을Leaf
, 중간의 Node들을Branch
Node라고 한다. - 위 예시에서 아래 쿼리를 실행시킨다고 했을 때 동작하는 방식이다.
- Btree의 Index를 탐색 후 Leaf Node가
Disk(file)
를 가리켜 해당 Disk 주소를 읽는다.
1
select name from table where age = 22;
- 그렇다면 name이 아닌 age를 조회한다면?
- Btree Index 탐색 후 Leaf Node가 존재한다면 Disk를 갈 필요 없이 결과를 보여주면 된다.
1
select age from table where age = 22;
- Page의 최대 크기를 3개의 데이터라고 가정
- 하나의 페이지에 3개의 데이터가 차있는 상태에서 23이라는 데이터를 넣어야 한다.
- 이 경우에는 하나의 Page로 부족하기에
**Page Split**
이 발생한다. - 홀수 = n/2+1 / 짝수 = n/2 번째 데이터를 상위 노드로 올린다.
- 이와 반대로, 데이터가 삭제 시 Page를 하나로 병합하는
**Page Merge**
가 발생한다.
Index Rebuild
- Index 생성 후 insert/update/delete가 반복되다보면, 성능이 저하됩니다. Tree의 Level이 깊어지고 한쪽으로 치우치는 현상이 발생하는거죠.
- 그렇기에, 주기적으로 index를 rebuild 해주는 작업이 필요합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- ORACLE
SELECT
I.TABLESPACE_NAME,
I.TABLE_NAME,
I.INDEX_NAME,
I.BLEVEL,
DECODE(SIGN(NVL(I.BLEVEL,99)-3),1,DECODE(NVL(I.BLEVEL,99),99,'?','Rebuild'),'Check') CNF
FROM USER_INDEXES I
WHERE I.BLEVEL > 4
ORDER BY I.BLEVEL DESC;
-- Mysql
SELECT
TABLE_SCHEMA,
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
CARDINALITY,
SEQ_IN_INDEX,
INDEX_TYPE,
COMMENT
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
SEQ_IN_INDEX > 4;
1
2
3
-- ALTER INDEX [인덱스명] REBUILD;
ALTER INDEX INDEX_NAME REBUILD;
Index를 설정한다고 무조건 빨라질까?
- 데이터 양이 적거나, Index가 비효율적(Index Scan이 테이블의 양고 비슷한 경우)으로 설정되어있다면 full scan이 빠르다.
-
위 내용을 생각해보자. index를 타게 되면 무조건 Tree를 순회 후 Disk를 접근해야 한다. 데이터 양이 적다면? 바로 Disk에 접속해서 다 찾아보는게 좋다.
Index가 많아지면, 조회 속도가 빨라질까?
- 앞에서도 계속 반복 되듯이, Index가 많아지면 Tree를 N번 돌게 되는것과 같다. 그렇기에 너무 많은 Index는 오히려 성능저하
Index 사용 시 주의해야할 쿼리들
- index range scan
- index column을 기반으로 범위를 지정해서 쿼리를 날리는 경우
index full scan
- multi column index에서 2번째 칼럼이 조건으로 사용된 경우
- index = age, name
1 2 3 4
select name from table where name = 'ㅇㅇ'
index skip scan
- Mysql 8.0 부터 지원하는 기능
- WHERE 조건절에 조건이 없는 인덱스의 선행 칼럼의 유니크한 값의 개수가 적어야함
- 쿼리가 인덱스에 존재하는 칼럼만으로 처리가 가능해야함.
- index = gender, age
- age > 15 조건을 검색 후 gender 조건을 검색
- https://wisdom-and-record.tistory.com/137
1
2
3
4
5
6
7
set optimizer_switch='skip_scan=off'
select
gender,
age
from table
where age > 15
- 조건절 변경
- 연산 값
- stored function
- type
1 2 3
where date(column) = '2023-01-01' where column = function()
-
부정형
1 2 3 4
not in <> not between is not null
-
뒷부분 일치
1
like %test
- count(*)
- multi column에서 순서를 바꿔서 조회하는 경우 ( optimizer가 있다면 큰 상관 없다. )
- index = age, name
1 2 3 4
select name from table where name = 'ㅇㅇ' and age = 15
- multi column index는 순서도 중요하니 잘 설계해야한다.
- Lock에도 영향을 줌으로 잘 설계해야한다.
참조
- https://kyungyeon.dev/posts/66