이번시간에는 인덱스에 관련된 강의 내용을 정리하며 인덱스가 무엇이며 어떤 특징과 동작을 하는지에 대해 자세하게 알아보았습니다
데이터베이스
테이블
데이터베이스는 데이터의 집합체이며 그 데이터를 관리하기 위해 로그들로 구성되어있다
데이터베이스에 포함되어 있는 개체
테이블은 데이터베이스의 작은 단위이며 테이블 또한 데이터들의 집합
Query SELECT 문을 날리면 이 Query는 어디로 날아갈까
페이지
테이블에 데이터를 INSERT하게 되면 INSERT가 된 RECORD는 해당 행, 페이지에 기록이 됨
페이지의 8KB 안에 INSERT한 레코드가 기록이 되고 우리가 데이터를 SELECT 할때 이 페이지의 RECORD가 SELECT되는 것임
만약 8KB안에 고정된 사이즈에 10개의 레코드가 들어가 있으면 ?
만약 100개의 레코드가 들어가 있으면 ?
즉, 한개의 페이지내에 레코드가 많을 수록 I/O 효율이 증가한다는 의미가 이 의미
1개의 페이지 내에 100개의 레코드 VS 10개의 각 페이지 내에 10개의 레코드
위 둘 중 어떤것이 더 효율적일까 ?
SELECT 시 최소의 페이지를 SELECT 할때 효율이 좋아지고 성능이 좋아짐
많고 많은 알고리즘이 결국 이 페이지 검색을 최소화 하기 위함 - 탐색 범위를 줄인다
1개의 페이지를 조회하는 시간은 하드웨어적으로 정해져있고 소프트웨어로 조회할 페이지를 최소화하는것이 개발자가 해야할 일 같다
페이지는 번호를 가지고 있음 0-N , 페이지 번호는 순차적임
이제 중요한 인덱스에 대해 알아보자
인덱스가 뭘까 ???
이렇게 가정을 해보겠다
나는 축구를 좋아하니 아스널 VS 맨시티 빅매치에서 경기장에 시작도 전에 사람들이 모두 경기를 관람하기 위해 좌석에 앉아있는 상황이다
이때 경기장에서 이종훈을 찾아야 한다면 …?
혼자 찾을 생각에, 경기장을 전부 뒤져볼 생각에 막막하던 찰나
경비아저씨께 여쭤보았다
그랬더니 기적이 ?
D열 14번 좌석에 있단다
경비아저씨의 이름은 ??
인덱스
인덱스는 —- 이다
빨리빨리
이유는 ?
인덱스도 페이지를 가지고 있음
데이터 베이스에서 페이지를 가지고 있는 개체는 딱 2가지
결국 디스크의 물리적인 공간을 차지 하는 것은 2가지 밖에 없음
뷰에는 사이즈가 없음
뷰는 테이블에 저장되어 있음
인덱스를 이해하자!!
만약 내가 인턴을 한다면 회사 내 데이터베이스의 테이블이 몇개일까 ?
수백개 수천개일 것이다
그리고 이들은 둘중에 하나의 형태를 띈다
힙의 형태 VS 클러스터형 인덱스
힙은 데이터를 가지고 있는 테이블이다
SELECT를 할때 누군가가 도와줘야한다
도와주는 사람이 비 클러스터형 인덱스
클러스터형 인덱스(Clustered Index)
비 클러스터형 인덱스(Nonclustered Index)
인덱스 생성
빨간 화살표는 정렬의 의미임
테이블에다가 인덱스를 추가하는 것임
비 클러스터형 인덱스는 최대 999개 까지 가능함
힙은 정렬의 기준 없이 저장 된 테이블의 존재 형태
클러스터 형 인덱스가 없는 테이블
힙의 장점과 단점
INSERT문이 좋아하는 테이블의 형태
SELECT문이 싫어하는 테이블 형태
TABLE SCAN
즉, 힙을 관리를 해줘야 함
클러스터 형 인덱스
부자인 이유 - 데이터를 가지고 있기 때문
힙 → 클러스터형 인덱스
로 형태를 바꿔버리자!
클러스터 형 인덱스란
클러스터형 인덱스가 정말 강력한 이유
힙 테이블들을 클러스터 형 인덱스로 바꾼다면 ?
그러면 SQL 서버는 힙 데이터들을 다 없애고 다른 공간에 새로운 데이터를 만듦
클러스터가 다른곳에 만들어짐
힙 테이블을 이름 컬럼 기준으로 클러스터형 인덱스로 바꾼다면 ?
아래와 같이 바뀜
리버
를 찾는다면 ?
Clustered Index Seek가 발생하면서 바로 찾아낼 수 있음
힙 테이블 VS 클러스터형 테이블
회사에 100개 테이블 중
70개 테이블이 힙형태이고 30개가 클러스터형 인덱스 테이블이라면 ?
이유가 있다면 좋겠지만 그게 아니라면 고민을 해봐야 함
만약 변경 비용이 많이 들더라도 이후의 SELECT를 위해서는 변경해야할 수도 있음
절반은 힙, 절반은 클러스터인 형태의 테이블들을 없음
둘중 하나만을 선택해야함 - 이유가 뭘까 ?
SCAN은 같은 것, 결국 다 읽는 것
좋은 것은 Seek임
Clustered Index Seek
Clustered Index Scan
Seek 딱 / Scan : 쫙~~
인덱스는 아래처럼 트리 형태의 구조를 가지고 있다
삼각형의 끝을 루트페이지 아래를 리프 페이지라 부른다
UNIQUE CLUSTERED INDEX
UNIQUE 여부를 명확히 설정
만약 인덱스를 만들때 중복된 값을 신경쓰지 않고 UNIQUE를 주지 않으면
클러스터는 내부적으로 데이터를 정렬해야 하므로 유일성 확보하기 위한 값들을 할당함 = 공간 낭비
예를 들어 동일한 테이블 두개를 만들때 1번은 클러스터형 테이블을 만들고 2번은 클러스터형 UNIQUE 를 만든다면
두 테이블의 사이즈가 다르다
UNIQUE를 안다면 UNIQUE 속성을 부여하자 !!
만약 WHERE절에 이메일이 많이 언급이 된다면 이메일 컬럼에 Non Clustered를 만들면 됨
그렇게 되면 이메일 데이터를 해당 비 클러스터 인덱스가 다 가지고 있는 것임
힙은 무작위로 데이터들이 존재하기 때문에 가장 좋은것은 주소를 딱! 찍어주는 것이 좋음
몇번째 파일에 몇번째 페이지에 몇번째 행인지를 알아야함
ID : 35 번을 찾는다고 가정하면
1:101:2
이므로 1의 101페이지의 2번째 행 으로 바로 찾을 수 있으므로 매우 빠르게 찾을 수 있다.
하지만 만약 데이터를 1000개 찾아야 한다면 ?
여기서 1:101:4 에서 1이 의미하는 것이 mdf
찾을때는 힙의 주소를 알고 있기 때문에 빠르게 찾을 수 있음
21번 을 찾기 위해서는?
하지만 위에서도 언급하였지만 바로 찾는것은 좋지만 만약에 찾는 개수가 100개 라면 ?
저 딱! (Index Seek) 과정을 100번 반복해야하므로 비용이 크다는 점을 알고가자
데이터를 바로 찾는 것을 Index Seek라고 함
RID를 가지고 찾아가는 과정을 RID Look Up 이라고 함
RID를 찾는데 왜 테이블이 힙이지 ?
클러스터형 인덱스 테이블에서는 클러스터 인덱스를 활용
하면 되지만
힙 테이블에서는 비 클러스터 인덱스 RID를 활용
하기 때문에
Index Seek, Clustered Index Seek, RID Look Up, Full Table Scan, Clustered Index Scan, 등등
용어속에 테이블 구조가 들어가 있음
만약 회사에서 전부 클러스터 인덱스 테이블
을 사용한다면 RID LooK Up
이라는 용어를 사용할 일은 절대 없음
이런 예시가 가능
우리 회사는 테이블 구조가 힙이기 때문에 빈번한 RID Look Up
으로 인해 성능에 문제가 생길 수 있다
Index Seek
를 통해서 찾는건 좋은데 RID Look Up
이 문제다
왜냐하면 위에서 설명한 100개 데이터를 찾을 때 딱! 을 100번 반복해야 하므로
비 클러스터 형 인덱스가 가진것
클러스터형 키 열을 갖는 이유
999개 까지 만들수 있음
힙은 연결되어있지 않아서 주소를 찍어줘야 하지만
클러스터형 인덱스는 정렬되어 있음
전화번호부(클러스터형 인덱스)에서 이종훈
을 찾는다고 가정하면
몇번째 페이지에 몇번째 행에 이종훈이 있다고 알려주는 것이 아님
전화번호부는 이미 이름순으로 정렬이 되어있으므로 이종훈
을 찾아보라고 말한다
다시 말해, 비 클러스터형 인덱스가 RID를 알려줄 이유가 없는 것이다
그리고 새로운 데이터를 넣을때
클러스터형 인덱스는 정렬 기준이 있기때문에 특정 페이지에 들어가야 하는 경우 다 차있으면 그 페이지를 분할하게 됨 그러면 나머지 데이터들은 옮겨가게 되고 그러면서 주소 RID가 계속 바뀌게 되는 점이 존재한다
이런 복잡한 과정이 있으므로 클러스터형 인덱스는 RID를 포기하고 인덱스 키 값을 받는 것이다
데이터가 정렬되어 있으므로 !
클러스터는 이름을 기준으로 정렬되어 있음 - 이름에 클러스터가 걸려있음
비 클러스터는 ID과 이름을 가지고 있음 - RID가 없음
비 클러스터형 인덱스가 언제 왜 사용되는지 알아보자
클러스터형 인덱스의 엄청난 테이블이 있고 쿼리가 이 테이블에 엑세스 한다
그런데 갑자기 엄청난 테이블이 하나 만들어 졌다 (비 클러스터형 인덱스, 2개의 컬럼만 가지고 있는 별도의 테이블)
왜냐하면 이 테이블은 ID로 정렬되어있으며마치 ID 와 이름을 가진 클러스터형 인덱스 같다
이 테이블은 ID로 정렬된 테이블이고 레코드 길이는 8KB로 제한되어 있으므로 한 번 레코드를 조회할 때
이 차이가 매우 크기때문에 인덱스를 사용해야 한다
Index Seek
Key LookUp
Clustered Index Seek
클러스터 인덱스 리프 페이지를 다 읽으면 ?
비 클러스터 인덱스 리프 페이지를 다 읽으면 ?
USE bigdata
CREATE TABLE cluster_table
(
id int not null,
name varchar(17) not null
);
go
insert into cluster_table values (1, '이종훈1');
insert into cluster_table values (2, '이종훈2');
insert into cluster_table values (3, '이종훈3');
insert into cluster_table values (11, '이종훈4');
insert into cluster_table values (12, '이종훈5');
insert into cluster_table values (21, '이종훈6');
insert into cluster_table values (22, '이종훈7');
insert into cluster_table values (31, '이종훈8');
insert into cluster_table values (32, '이종훈9');
go
ALTER TABLE cluster_table ADD CONSTRAINT PK_cluster_table_id PRIMARY KEY (id)';
- 클러스터형 인덱스를 구성하기 위해서 행 데이터를 해당 id열로 정렬한 후, 루트 페이지를 만든다
- 클러스터형 인덱스는 루트 페이지와 리프 페이지로 구성되며, 리프 페이지는 데이터 그 자체이다
- 클러스터형 인덱스는 검색 속도가 비 클러스터형 인덱스 보다 더 빠르다
USE bigdata
CREATE TABLE cluster_table
(
id int not null,
name varchar(17) not null
);
go
insert into cluster_table values (1, '이종훈1');
insert into cluster_table values (2, '이종훈2');
insert into cluster_table values (3, '이종훈3');
insert into cluster_table values (11, '이종훈4');
insert into cluster_table values (12, '이종훈5');
insert into cluster_table values (21, '이종훈6');
insert into cluster_table values (22, '이종훈7');
insert into cluster_table values (31, '이종훈8');
insert into cluster_table values (32, '이종훈9');
go
ALTER TABLE cluster_table ADD CONSTRAINT PK_cluster_table_id PRIMARY KEY (id)';
- 비 클러스터형 인덱스는 데이터 페이지를 건들지 않고, 별도의 장소에 인덱스 페이지를 생성한다
- 인덱스 페이지의 리프 페이지에 인덱스로 구성항 열을 정렬한 후 위치 포인터(RID)를 생성한다
테이블과 인덱스
알 품 인
RID를 품은 인덱스
힙 형태의 테이블에 만들어진 비 클러스터형 인덱스
키 품 인
Key를 품은 인덱스
클러스터 형 인덱스가 있는 테이블 위에 만들어진 비 클러스터형 인덱스
즉 클러스터가 만든 비 클러스터 인덱스
각자 RID, KEY를 가지고 있음 왜 ? 변하지 않을거라 생각하기 때문에
그런데 만약 클러스터의 Key컬럼을 UPDATE를 통해 바꾼다면 ?
그러면 키품인이 난리가 남
RID가 바뀔 것 같아서 KEY를 품었더니 이제 KEY가 바뀌네 ?
여기서 RID가 바뀔 것 같은 이유는 ?
인덱스는 데이터를 가진 테이블이다 !!
쿼리를 테이블이 아닌 인덱스로 날리면
인덱스에서 먼저 찾고 인덱스에 없다면 테이블로 가게된다
참고 - SQL Unplugged 2013