# 인덱스

# 인덱스(Index)란?

인덱스는 데이터베이스의 테이블에 대한 검색 속도를 향상시켜주는 자료구조입니다.
테이블의 특정 컬럼에 인덱스를 생성하면, 해당 컬럼의 데이터를 정렬한 후 별도의 메모리 공간에 컬럼의 값과 물리적 주소를 키-값 형태로 저장합니다.
쉽게 말해, 인덱스는 데이터를 빠르게 찾을 수 있는 하나의 장치라고 생각하면 됩니다.

비슷한 예로는 책의 마지막 장에 있는 찾아보기가 있습니다. 책의 본문이 있고 그 본문 안에 내가 찾고자 하는 항목을 찾아보기를 통해 빠르게 찾을 수 있습니다.
이와 마찬가지로 인덱스를 설정하면 테이블 안에 내가 찾고자 하는 데이터를 빠르게 찾을 수 있습니다.

인덱스

# 인덱스의 장점

테이블을 검색하는 속도와 성능이 향상됩니다. 또 그에 따라 시스템의 전반적인 부하를 줄일 수 있습니다.

핵심은 인덱스에 의해 데이터들이 정렬된 형태를 갖는다는 것입니다. 기존엔 Where문으로 특정 조건의 데이터를 찾기 위해서 테이블의 전체를 조건과 비교해야 하는 '풀 테이블 스캔(Full Table Scan)' 작업이 필요했는데, 인덱스를 이용하면 데이터들이 정렬되어 있기 때문에 조건에 맞는 데이터를 빠르게 찾을 수 있습니다. 또 ORDER BY 문이나 MIN/MAX 같은 경우도 이미 정렬이 되어 있기 때문에 빠르게 수행할 수 있습니다.

# 인덱스의 단점

  • 인덱스를 관리하기 위한 추가 작업이 필요합니다.
  • 추가 저장 공간이 필요합니다.
  • 잘못 사용하는 경우 오히려 검색 성능을 저하할 수 있습니다.

인덱스를 항상 정렬된 상태로 유지해야 하기 때문에 인덱스가 적용된 컬럼에 삽입, 삭제, 수정 작업을 수행하면 추가 작업이 필요합니다.

  • 삽입(INSERT) : 새로운 데이터에 대한 인덱스를 추가
  • 삭제(DELETE) : 삭제하는 데이터의 인덱스를 사용하지 않는다는 작업 수행
  • 수정(UPDATE) : 기존의 인덱스를 사용하지 않음 처리, 갱신된 데이터에 대한 인덱스 추가

이처럼 인덱스의 수정도 추가적으로 필요하기 때문에 데이터의 수정이 잦은 경우 성능이 낮아집니다. 또 데이터의 인덱스를 제거하는 것이 아니라 '사용하지 않음'으로 처리하고 남겨두기 때문에 수정 작업이 많은 경우 실제 데이터에 비해 인덱스가 과도하게 커지는 문제점이 발생할 수 있습니다. 따라서 추가 저장 공간이 많이 필요하게 됩니다.

또한 인덱스는 전체 데이터의 10 ~ 15% 이상의 데이터를 처리하거나, 데이터의 형식에 따라 오히려 성능이 낮아질 수 있습니다. 예를 들어 나이나 성별과 같이 값의 range가 적은 컬럼인 경우, 인덱스를 읽고 나서 다시 많은 데이터를 조회해야 하기 때문에 비효율적입니다.

# 인덱스를 사용하면 좋은 경우

  • 규모가 큰 테이블
  • 삽입, 삭제, 수정 작업이 자주 발생하지 않는 컬럼
  • WHERE나 ORDER BY, JOIN 등이 자주 사용되는 컬럼
  • 데이터의 중복도가 낮은 컬럼

# 인덱스의 자료구조

인덱스는 보통 B-트리라는 자료 구조로 이루어져 있습니다.
B-트리에 대해 간단히 설명하자면, 루트 노드, 리프 노드, 그리고 루트 노드와 리프노드 사이에 있는 브랜치 노드로 구성되어 있습니다.
만약, E를 찾는다면 전체 테이블을 탐색하는 것이 아니라 E가 있을 법한 리프노드로 들어가서 E를 탐색하는 방식입니다.
이 자료 구조 없이 E를 탐색하고자 하면 A부터 순서대로 5번을 탐색해야 하지만, 이렇게 노드들로 나누면 2번 만에 리프노드에서 찾을 수 있습니다.
b트리

대수확장성

트리 깊이가 리프 노드 수에 비해 매우 느리게 성장하는 것을 의미합니다.
기본적으로 인덱스가 한 깊이씩 증가할 때마다 최대 인덱스 항목의 수는 4배씩 증가합니다. 이런 대수확장성 덕분에 인덱스가 효율적입니다.

# 인덱스 생성 방법

  • MySQL

    • 클러스터형 인덱스
      • 테이블당 하나를 설정할 수 있습니다.
      • PRIMARY KEY 옵션으로 기본키로 만들거나, 기본키로 만들지 않고 unique not null 옵션을 붙이면 클러스터형 인덱스로 만들 수 있습니다.
      • 보조 인덱스보다 검색 속도는 빠르지만, 데이터 CUD는 더 느립니다.
    • 세컨더리 인덱스
      • create index 명령어를 기반으로 만들 수 있습니다.
      • 보조 인덱스로 여러 개의 필드 값을 기반으로 쿼리를 많이 보낼 때 생성해야 하는 인덱스입니다.
  • MongoDB

    • 도큐먼트를 만들면 자동으로 ObjectID가 형성되며, 해당 키가 기본키로 설정됩니다.
    • 세컨더리키도 부가적으로 설정해서 기본키와 세컨더리키를 같이 쓰는 복합 인덱스를 설정할 수 있습니다.

# 인덱스 최적화 기법

  1. 인덱스는 비용인 것을 명심해라.
    인덱스는 인덱스 리스트, 컬렉션 순으로 탐색하기 때문에 필수적으로 2번 탐색이 진행됩니다. 때문에, 관련 읽기 비용이 들게 됩니다. 또한, 컬렉션이 수정되었을 때 인덱스도 수정되어야 합니다. 이때 B-트리의 높이를 균형 있게 조절하는 비용도 들고, 데이터를 효율적으로 조회할 수 있도록 분산시키는 비용도 들게 됩니다.
    따라서, 쿼리에 있는 필드에 인덱스를 무작정 다 설정하면 안됩니다. 컬렉션에서 가져와야하는 양이 많을수록 인덱스를 사용하는 것은 비효율적입니다.
  2. 항상 테스팅하라.
    • 서비스에서 사용하는 객체의 깊이, 테이블 양이 다르기 때문에 인덱스 최적화 기법은 서비스 특징에 따라 달라집니다
    • explain()함수를 통해 인덱스를 만들고 쿼리를 보낸 이후에 테스팅을 하며 걸리는 시간을 최소화해야 합니다.
    EXPLAIN
    SELECT * FROM t1
    JOIN t1 ON t1.c1 = t2.c1
    
  3. 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순이다.
    • 보통 여러 필드를 기반으로 조회를 할 때 복합 인덱스를 생성하는데, 이 인덱스를 생성할 때는 순서가 있고 생성 순서에 따라 인덱스 성능이 달라집니다.
    • 같음, 정렬, 다중 값, 카디널리티 순으로 생성해야 합니다.
      1. 어떠한 값과 같음을 비교하는 ==이나 equal이라는 쿼리가 있다면 제일 먼저 인덱스로 설정합니다.
      2. 정렬에 쓰는 필드라면 그 다음 인덱스로 설정합니다.
      3. 다중 값을 출력해야 하는 필드, 즉 쿼리 자체가 > 이거나 < 등 많은 값을 출력해야 하는 쿼리에 쓰는 필드라면 나중에 인덱스를 설정합니다.
      4. 유니크한 값의 정도를 카디널리티라고 합니다. 이 카디널리티가 높은 순서를 기반으로 인덱스를 생성해야 합니다.

# 참고자료