한 줄 정의

인덱스는 칼럼의 값을 미리 정렬해 보관 하는 자료구조이며, 검색 성능을 얻기 위해 저장 공간과 쓰기 비용을 지불하는 트레이드오프입니다.

쉽게 말하면

인덱스는 책 뒤의 색인 과 똑같습니다.

  • 본문(테이블) = 페이지 순서대로 쓰여진 내용
  • 색인(인덱스) = “단어 → 페이지 번호” 매핑을 알파벳순으로 정렬 해놓은 별도의 페이지

정렬되어 있기 때문에 “이진 탐색”이 가능하고, 그 덕분에 수백만 건 중에서도 원하는 항목을 몇 번의 비교만으로 찾을 수 있습니다.

왜 중요한가?

인덱스는 “있으면 빠르다”가 아니라 “어떻게 만들어졌는지”가 성능을 결정 합니다.

  • 같은 칼럼에 걸어도 자료구조(B-Tree/Hash/R-Tree/Full-text) 에 따라 동작이 다릅니다
  • 같은 B-Tree여도 복합 인덱스의 칼럼 순서 에 따라 사용 여부가 달라집니다
  • 유니크 여부 에 따라 옵티마이저의 판단이 달라집니다

그래서 “인덱스 = 검색 빠르게 하는 것” 수준의 이해로는 부족하고, 분류 기준별 특성 을 알아야 합니다.

핵심 내용

인덱스의 본질: 정렬

인덱스의 핵심은 정렬된 상태로 저장 입니다. 정렬되어 있기 때문에:

  • 이진 탐색이 가능합니다 (O(log N))
  • 범위 검색이 가능합니다 (시작점 찾고 순차 스캔)
  • 정렬 결과 반환이 공짜입니다 (ORDER BY)

DBMS의 인덱스는 SortedList가 아니라 B-Tree 를 쓰는데, 이는 정렬된 상태를 유지하면서도 중간 삽입 비용 을 낮추기 위함입니다.

SortedList vs B-Tree
자료구조검색삽입
SortedList (배열)빠름매우 느림 (중간 삽입 시 전체 이동)
B-Tree빠름적절함 (분리/병합으로 국소적 변경)

DB는 삽입/삭제가 빈번하므로 SortedList는 쓸 수 없습니다. B-Tree가 기본 자료구조가 된 이유입니다.

인덱스의 분류 기준

인덱스는 다음 4가지 축으로 분류할 수 있으며, 하나의 인덱스는 각 축에서 하나씩 속성 을 가집니다.

flowchart TD
    IDX["MySQL 인덱스"]

    subgraph AX1["① 역할"]
        PK["프라이머리 키"]
        SEC["세컨더리 인덱스"]
    end

    subgraph AX2["② 저장 방식 (자료구조)"]
        BT["B-Tree"]
        HS["Hash"]
        RT["R-Tree"]
        FT["Full-text"]
    end

    subgraph AX3["③ 중복 허용"]
        UN["Unique"]
        NU["Non-unique"]
    end

    subgraph AX4["④ 기능"]
        FN["함수 기반"]
        MV["멀티 밸류"]
        SP["공간 검색"]
    end

    IDX --> AX1
    IDX --> AX2
    IDX --> AX3
    IDX --> AX4
① 역할: 프라이머리 키 vs 세컨더리 인덱스
구분프라이머리 키세컨더리 인덱스
개수테이블당 1개여러 개 가능
NULL불가가능
중복불가가능 (유니크가 아니면)
InnoDB클러스터링 인덱스 (데이터 그 자체)리프에 PK 값 저장

InnoDB에서 PK는 특별합니다. 데이터를 PK 순서로 물리적으로 저장 하기 때문에, PK는 단순한 인덱스가 아니라 테이블의 저장 구조 그 자체 입니다.

② 저장 방식: 자료구조별 특성
자료구조적합한 용도특징
B-Tree일반적인 등가/범위 검색정렬된 상태 유지, 디스크 I/O에 최적화
Hash정확히 일치하는 값 검색매우 빠른 등가 검색, 범위 검색 불가
R-Tree2차원 공간 데이터MBR(Minimum Bounding Rectangle) 기반
Full-text긴 문장/문서의 키워드 검색단어 단위 역색인
  • Hash 인덱스 는 MySQL 메모리 엔진이나 InnoDB의 어댑티브 해시 인덱스 (자동 생성)에서 쓰입니다. 등가 비교만 가능해서 용도가 제한적입니다
  • 대부분의 일반 테이블 인덱스는 B-Tree 입니다
③ 중복 허용: Unique vs Non-unique
  • Unique 인덱스 는 성능 최적화가 아니라 제약 조건 을 위한 것입니다
  • 옵티마이저는 유니크 인덱스에서 값을 찾으면 “한 건 더 있을까?”를 확인하지 않고 멈춥니다. 이것이 약간의 성능 이점을 주긴 합니다
  • 하지만 변경 시 중복 체크 비용 이 추가되어 전반적으로는 오히려 느립니다

자세한 함정은 Ch08-9 유니크 인덱스에서 다룹니다.

④ 기능: 함수/멀티 밸류/공간

MySQL 8.0 이후 추가된 고급 기능들입니다.

  • 함수 기반 인덱스 : WHERE SUBSTRING(name,1,3)='Geo' 같은 조건을 인덱스로 처리
  • 멀티 밸류 인덱스 : JSON 배열 필드에 대한 인덱스
  • 공간 인덱스 : GEOMETRY 타입 (R-Tree 기반)

인덱스의 대가: 쓰기 성능

인덱스는 공짜가 아닙니다. 인덱스 하나당 INSERT/UPDATE/DELETE 비용이 증가합니다.

작업비용
INSERT데이터 + 인덱스 개수만큼의 B-Tree 삽입
DELETE데이터 + 인덱스 개수만큼의 B-Tree 마킹 (퍼지는 백그라운드)
UPDATE변경된 칼럼에 해당하는 인덱스만 재구성

특히 UPDATE는 변경된 칼럼의 인덱스만 재구성하므로, 자주 변경되는 칼럼을 인덱스에 포함시키지 않는 것이 유리합니다.

선택도(Cardinality)

인덱스 효율의 핵심 지표는 선택도(= 유니크한 값의 비율) 입니다.

  • 선택도가 높다 = 인덱스 한 번 탐색으로 적은 레코드만 골라낼 수 있다 = 효율적
  • 선택도가 낮다 = 인덱스를 타도 많은 레코드를 읽어야 한다 = 비효율적
예: 1,000,000건 테이블
- gender (M/F): 선택도 2 → 평균 500,000건 → 인덱스 비효율
- email: 선택도 ≈ 1,000,000 → 1건만 반환 → 인덱스 매우 효율

일반적으로 전체의 20~25% 이상 을 읽어야 하는 조건이면 옵티마이저는 풀 스캔을 선택합니다. 랜덤 I/O 인덱스 스캔보다 순차 I/O 풀 스캔이 더 빠르기 때문입니다.

정리

인덱스 자료구조 비교

자료구조등가 검색범위 검색정렬주요 용도
B-TreeOOO일반 인덱스
HashOXX메모리 테이블, 어댑티브 해시
R-TreeXO (공간)X공간 데이터
Full-textXXX문서 검색

내 생각

  • “테이블에 칼럼이 많다 = 인덱스가 많이 필요하다”는 오해 를 자주 봅니다. 실제로는 WHERE/ORDER BY/JOIN에 자주 등장하는 몇 개 칼럼에 복합 인덱스 1~2개 로 대부분 해결됩니다.

  • 인덱스 추가 요청이 들어올 때, 반사적으로 “네”라고 하지 말고 “이 칼럼의 선택도가 얼마인가?” 를 먼저 확인해야 합니다. SELECT COUNT(DISTINCT col)/COUNT(*) FROM tab; 한 줄로 알 수 있습니다.

  • 자료구조별 특성을 기억하는 가장 쉬운 방법은 “무엇이 불가능한가” 로 외우는 것입니다. Hash는 범위 불가, R-Tree는 등가 불가, Full-text는 일반 검색 불가. 이렇게 외우면 어느 조건에 어느 인덱스를 써야 하는지 자연스럽게 나옵니다.

관련 개념

출처

  • Real MySQL 8.0 (1권), 8.2 인덱스란?