오늘은 예전부터 궁금해 했던 데이터베이스 INDEX에 대해 알아보고자 한다.
이 글은 우아한 Tech에서 진행한 10분 테코톡을 시청한 뒤 정리한 내용을 적을 것이다. 테코톡 짱👍
사전적 정의로는 색인이다.
예를 들어 자바의 정석 책의 다형성 파트를 찾아보고 싶을때 ㄱㄴㄷ 순으로 정렬된 찾아보기 페이지에 가서 ㄷ-ㄹ에 가서 다형성을 찾을 수 있다.
이처럼 원하는 값을 빠르게 찾는다는 것에 초점이 맞춰져 있다.
이를 데이터베이스에도 적용하면 어떻게 될까?
SELECT, INSERT, UPDATE, DELETE 중 '찾는다'에 해당하는 SELECT에 활용할 수 있다.
현재 이름, 성별, 이메일 등 백만 건 이상 데이터가 인덱스 기준이 하나도 잡혀있지 않을 때 이메일을 통해 조회를 해본다고 가정한다. 이때 전체 데이터에서 순차적으로 확인하기 때문에 매우 느릴 것이다.
-> 현재 데이터는 기준 없이 저장된 상태이기 때문
-> 만약에 데이터가 특정 기준으로 정렬되어 있다면 검색을 빠르게 할 수 있을 것이다.
이의 경우 현재 데이터는 이메일로 정렬된 백만건 이상의 데이터로 바뀔 것이다.
여기서 다시 이메일을 통해 조회하게 되면 속도가 매우 빨라질 것이다.
SELECT * FROM member WHERE email = 'asebn121@gmail.com'
SELECT * FROM member
💡 인덱스는 데이터베이스 테이블에 대한 검색 성능을 향상시키는 자료 구조이며 WHERE 절 등을 통해 활용된다.
데이터가 저장되는 단위(16 Kbyte -> MySQL 기준(데이터베이스 환경마다 다를 수 있다))
💡 Full Table Scan 사용?
1. 적용 가능한 인덱스가 없는 경우
2. 인덱스 처리 범위가 넓은 경우
3. 크기가 작은 테이블에 엑세스 하는 경우
이진탐색과 연결리스트의 장점이 합쳐져 만들어진 자료구조
균형있는 이진탐색트리의 경우 검색 시간복잡도는 O(log n)이다.
하지만 균형 없는 이진탐색 트리의 시간복잡도는 최악의 경우 O(n)이다.
이는 이진탐색트리의 장점을 살렸다고 볼 수 없다.
이러한 이진탐색트리의 단점을 극복하기 위해서 여러 자료구조가 나왔고 그 중 하나가 "B-Tree"이다.
최상단에는 루트 페이지가 오며
브랜치 페이지는 루트 페이지와 리프 페이지 사이에 여러 개가 올 수 있다.
최하단에는 리프 페이지가 온다.
이를 통해 SELECT의 성능이 향상되는 것을 알 수 있게 되었다.
❗ 그렇다면 INSERT, UPDATE, DELETE는 어떻게 될까?
인덱스의 데이터를 실제로 지우지 않고 사용안함 표시를 한다.
WHERE 절로 처리할 대상을 찾기 위한 조회 성능은 향상된다.
실제 데이터와 무리를 이름
실제 데이터와 같은 무리의 인덱스
실제 데이터와 무리를 이루지 않음
-> 실제 데이터가 정렬된 사전과 같은 역할을 하는게 클러스터링 인덱스
실제 데이터와 다른 무리의 별도의 인덱스
-> 앞에서 설명했던 책 내용을 찾기 위해 책 뒤에 별도로 존재했던 찾아보기 페이지와 같은 역할을 하는게 논-클러스터링 인덱스
😲 놀라운 사실은 우리도 모르는 새에 데이터베이스 인덱스를 사용하고 있었다.
CREATE TABLE member (
id int primary key,
name varchar(255),
email varchar(255) unique,
);
위와 같이 테이블만 생성을 해도 자동으로 인덱스가 생성이 된다.
-> PK와 UNIQUE 제약 조건 때문
한 컬럼에 PK를 적용하면 클러스터링 인덱스가 자동으로 생성되게 된다.
그리고 한 컬럼에 unique 제약조건을 걸게 되면 논-클러스터링 인덱스가 자동으로 생성이 된다.
ALTER TABLE member
ADD CONSTRAINT pk_id PRIMARY KEY (id);
ALTER TABLE member MODIFY COLUMN id int NOT NULL;
ALTER TABLE member ADD CONSTRAINT nuq_id UNIQUE (id);
ALTER TABLE member
ADD CONSTRAINT unq_name UNIQUE (name);
CREATE UNIQUE INDEX unq_inx_name
ON member (name);
CREATE INDEX idx_name
ON member (name);
"도리" 옆에 있는 숫자 (1002 + #3)에서 1002는 실제 데이터 페이지의 주소를 의미하고 옆에 3은 1002 페이지의 세 번째에 "도리"에 대한 데이터가 존재한다는 주소를 의미한다.
데이터 페이지의 주소 값이 아닌 클러스터링 인덱스가 적용된 id 컬럼의 값이 적용되어 있다. "도리"의 id는 6이기 때문에 6을 담고 있고 "라라"는 12를 담고 있다.
만약 id가 3인 파랑의 데이터가 추가로 삽입됐다고 해보자
그러면 id를 기준으로 정렬되야하기 때문에 "파랑"의 데이터는 "제로" 밑에 들어와야 한다. "호호"와 "스컬은" 4, 5번이기 때문에 페이지 분할이 발생하게 된다.
그렇게 되면 name 인덱스 페이지에서 "스컬"과 "호호"의 주소는 변경되어야 한다.
데이터가 추가되거나 삭제될 때마다 이 인덱스 페이지의 주소들을 계속해서 변경해야 하는 영향을 주기 때문에 id가 직접 변경되지 않는 한 인덱스 페이지에 영향을 주지 않도록 구성하고 있다.
어떤 컬럼에 인덱스를 적용해야 할까?
사전적 의미: 그룹 내 요소의 개수
카디널리티(그룹 내 요수의 개수)가 높은것
= 중복도가 낮은 것
💡 사용하면 좋은 경우
1. 카디널리티가 높은 (중복도가 낮은) 컬럼
2. WHERE, JOIN, ORDER BY 절에 자주 사용되는 컬럼
- 인덱스는 추가 공간이 필요로 된다
- 조건 절이 없다면 인덱스가 사용되지 않는다.
3. INSERT / UPDATE / DELETE 가 자주 발생하지 않는 컬럼
4. 규모가 작지 않은 테이블
오늘은 인덱스에 대해서 알아봤다. 테코톡 덕분에 짧은 시간 내에 인덱스에 대해 이해할 수 있었던 것 같다. 나중에 데이터베이스 성능이 안나오면 인덱스를 적용해 볼 수 있을 것 같다!