본문 바로가기

DB

[DB] 인덱스 개념 및 원리

인덱스란?

인덱스의 개념을 설명할 때 가장 많이 드는 예시가 있다. 바로 책의 색인(Index)이다. 데이터베이스 공부를 하다가 RDBMS 개념이 궁금해 책을 찾아보고 있다고 가정해보자. 책을 첫 장부터 넘기면서 해당 내용을 찾을 수도 있지만 이는 매우 비효율적이다. 아마 대부분의 똑똑한 독자들은 책의 맨 뒤에 있는 색인 페이지에서 해당 개념이 포함되어 있는 페이지를 찾아 볼 것이다. 이처럼 책의 색인은 우리가 원하는 단어를 쉽고 빠르게 찾을 수 있게 도와준다. 이와 마찬가지로 데이터베이스에서 인덱스를 설정하면 테이블 안에 내가 찾고자 하는 데이터를 쉽고 빠르게 찾을 수 있다.

 

여기까지만 보면 인덱스를 사용하는게 매우 효과적으로 보이지만 무조건 인덱스를 사용하는 것은 오히려 성능을 악화시킬 수 있다. 예를 들어 데이터베이스 책에서 "데이터베이스"라는 단어를 색인 페이지에 만들어 놓으면 데이터베이스라는 단어는 거의 모든 페이지마다 존재할 것이기 때문에 오히려 배보다 배꼽이 커지는 상황이 발생한다.

인덱스 장점

  • 검색 속도가 향상될 수 있다. (단, 항상 그런것은 아님!!)
  • 검색 속도의 향상으로 쿼리의 부하가 줄어 시스템 전체의 성능이 향상된다.

인덱스 단점

  • 인덱스가 데이터베이스 공간을 차지하기 때문에 추가적인 공간이 필요하다. (대략 데이터베이스 크기의 10%)
  • 처음 인덱스 생성하는데 시간이 많이 소요될 수 있다.
  • 데이터의 변경 작업(Insert, Update, Delete)이 자주 일어나는 경우에는 오히려 성능이 악화된다.

인덱스 생성 방법

인덱스는 테이블의 컬럼 단위에 생성되는데 인덱스 생성 방법으로는 크게 두 가지가 있다.

  1. 제약 조건으로 자동 생성되는 인덱스
  2. CREATE INDEX [...] 명령어를 사용해 직접 생성하는 인덱스

자동 생성되는 인덱스의 경우 제약 조건으로 특정 컬럼에 Primary Key나 Unique를 설정하면 NORMAL 인덱스가 자동 생성된다. 해당 인덱스는 DROP INDEX 명령어로는 삭제할 수 없으며 삭제하려면 제약 조건을 제거하는 수밖에 없다.

 

직접 생성하는 인덱스의 경우 아래 예시 명령어처럼 인덱스를 생성하고 삭제한다.

/* 특정 테이블의 특정 컬럼에 고유(UNIQUE) 인덱스를 생성하라 */
CREATE UNIQUE INDEX 인덱스명 ON 테이블명 (컬럼명)

/* 특정 인덱스 삭제 */
DROP INDEX 인덱스명

인덱스 내부 작동 원리

여러 종류의 인덱스가 있지만 보통 B-Tree라는 자료 구조로 이루어져 있기 때문에 해당 글에서는 B-Tree 자료구조를 기반으로 정리했습니다. B-Tree 자료구조는 루트 노드, 브랜치 노드, 리프 노드로 이루어져 있으며 여기서 노드란 트리 구조에서 데이터가 존재하는 공간을 의미한다.

위와 같은 데이터가 데이터베이스에 저장되어 있다고 가정해보자. 인덱스가 없는 상황에서 MMM이라는 데이터를 조회하려면 AAA 부터 총 8건의 데이터를 검색해야 MMM을 조회할 수 있다.

 

하지만 아래와 같이 B-Tree 자료구조로 이루어진 인덱스가 생성되어 있다면 루트 노드에서 부터 검색을 시작하여 리프노드로 이동한 후 2개의 블록만 검색하면 되므로 5건의 데이터만 검색하면 MMM을 조회할 수 있다.

지금은 레벨이 2단계 뿐이라 크게 와닿지 않지만 만약 훨씬 많은 양의 데이터의 경우에는 그 차이가 기하급수적으로 난다.

 

B-Tree 자료구조가 조회 성능을 향상시키는데 매우 효율적임을 확인했다. 하지만 단점에서도 언급했듯이 데이터 변경 작업(Insert, Update, Delete)이 빈번하게 일어나는 곳에 인덱스를 적용하면 오히려 성능이 나빠지는데 이는 인덱스 분할 작업이 발생하기 때문이다.

 

위 데이터베이스에 III 데이터를 추가한다고 가정해보자. 다행히 해당 리프노드에 빈자리가 있어서 JJJ 데이터를 한칸 이동시킨 후 III가 추가되었다.

문제는 이 다음 GGG 데이터를 추가할 때 발생한다. GGG 데이터를 추가하면 FFF 블록 다음에 GGG를 추가해야 하는데 빈 공간이 없어 인덱스 분할 작업이 발생한다. 이어서 데이터를 추가하다보면 결국 루트노드에도 빈 공간이 없기 때문에 루트노드에서도 인덱스 분할이 일어나게 되고 결국 데이터를 추가한 것 만으로 연쇄적으로 많은 작업들이 발생하게 된다. 위와 같은 인덱스 분할 작업은 비용이 많이 들기 때문에 성능에 큰 영향을 주게 된다.

인덱스 특징

  • 인덱스 생성 시에는 데이터 블록은 그냥 둔 상태에서 별도의 블록에 인덱스를 구성한다.
  • 엔덱스의 리프 블록은 데이터가 아니라, 데이터가 위치하는 주소값(ROWID)이다.
  • 데이터의 입력/수정/삭제 시에는 인덱스가 없을 때보다 느리다.
  • 인덱스는 여러 개 생성할 수가 있다. 하지만, 함부로 남용할 경우 오히려 시스템 성능을 악화시킬 수 있다.
  • 인덱스를 검색하기 위해서는 반드시 WHERE 절에 해당 인덱스를 생성한 열의 이름이 나와야한다.

인덱스 사용 주의사항

  • WHERE 절에서 자주 사용되는 열에 인덱스를 만들어야 하며 생성, 변경, 삭제보다 조회가 자주 사용되는 곳에 인덱스를 생성해야 한다.
  • 데이터의 중복도가 높은 열에는 인덱스를 만들어도 효과가 없다. (카디널리티가 높은 순서를 기반으로 인덱스를 생성한다.)
  • 사용하지 않는 인덱스는 제거하자

'DB' 카테고리의 다른 글

[DB] 데이터베이스 정규화  (1) 2022.12.03
[DB] 트랜잭션과 격리 수준  (0) 2022.10.24