← 개인 공부 · Database
STUDY NOTE · 개념 정리

DB 인덱스 개념 정리: B+Tree, 클러스터드 vs 논클러스터드, 복합 인덱스

Database
인덱스는 특정 컬럼 값을 정렬해 둔 별도의 자료구조로, 책의 색인처럼 원하는 데이터의 위치를 빠르게 찾게 해준다. 조회는 빨라지지만 저장 공간과 쓰기 성능을 대가로 낸다.

1. 인덱스가 없으면

WHERE email = 'a@b.com'을 실행하면 DB는 테이블의 모든 행을 처음부터 끝까지 읽으며 비교한다(Full Table Scan). 100만 건이면 100만 번 비교한다. 인덱스가 있으면 정렬된 구조에서 몇 번 만에 위치를 찾는다.

2. 인덱스의 장단점

구분내용
장점조건 검색(WHERE), 정렬(ORDER BY), 그룹화(GROUP BY), 조인 속도 향상
단점추가 저장 공간 필요
단점INSERT / UPDATE / DELETE 시 인덱스도 함께 수정해야 해서 쓰기가 느려짐
단점잘못 만들면 옵티마이저가 쓰지 않거나 오히려 느려짐

3. 자료구조: 왜 B+Tree인가

자료구조등호 검색범위 검색특징
해시 테이블O(1)불가정렬이 없어 범위·정렬에 못 씀
이진 탐색 트리O(log n)가능한 노드에 값 1개라 트리가 깊어짐 (디스크 접근 많음)
B-TreeO(log n)가능한 노드에 여러 값, 트리가 낮음
B+TreeO(log n)매우 유리데이터는 리프에만, 리프끼리 연결 리스트

DB는 디스크(페이지) 단위로 읽기 때문에 트리 높이가 낮을수록 디스크 접근이 줄어든다. B+Tree는 노드 하나에 수백 개의 키를 담아 100만 건도 높이 3~4 정도로 끝난다. 또 리프 노드가 서로 연결돼 있어 범위 검색(BETWEEN, >) 시 시작점만 찾고 옆으로 읽으면 된다. MySQL InnoDB를 비롯한 대부분의 RDBMS가 B+Tree를 쓴다.

4. 클러스터드 vs 논클러스터드 인덱스

구분클러스터드 인덱스논클러스터드(세컨더리) 인덱스
개수테이블당 1개여러 개 가능
정렬 대상테이블 데이터 자체가 이 순서로 저장인덱스만 별도로 정렬
InnoDB에서PK (없으면 UNIQUE NOT NULL, 그것도 없으면 내부 키)PK 외의 인덱스
리프 노드 내용실제 행 데이터인덱스 컬럼 값 + PK 값
조회 과정한 번에 행에 도달인덱스에서 PK를 얻고 → 클러스터드 인덱스를 다시 탐색

세컨더리 인덱스로 찾은 행이 아주 많으면 "PK로 다시 찾아가기"가 그만큼 반복된다. 이 경우 옵티마이저는 인덱스를 버리고 풀 스캔을 고르기도 한다.

5. 복합 인덱스와 최좌측 접두사 규칙

INDEX (a, b, c)는 a로 정렬하고, a가 같으면 b로, b까지 같으면 c로 정렬한 구조다. 전화번호부가 성 → 이름 순으로 정렬된 것과 같다.

조건인덱스 사용
WHERE a = ?사용
WHERE a = ? AND b = ?사용
WHERE a = ? AND b = ? AND c = ?모두 사용
WHERE b = ?불가 (첫 컬럼 a가 없음)
WHERE a = ? AND c = ?a까지만 범위 축소, c는 필터로만
WHERE a > ? AND b = ?a 범위 이후의 b는 정렬 순서를 활용 못 함

설계 원칙은 두 가지다.

  1. 등호(=) 조건 컬럼을 앞에, 범위 조건 컬럼을 뒤에 둔다.
  2. 등호 조건끼리는 카디널리티(값의 종류)가 높은 컬럼을 앞에 두는 것이 일반적으로 유리하다.

6. 커버링 인덱스

쿼리에 필요한 컬럼이 모두 인덱스 안에 있어서 테이블 본체를 읽지 않고 인덱스만으로 결과를 내는 경우다. 세컨더리 인덱스 → 클러스터드 인덱스로 가는 과정이 사라져 크게 빨라진다. EXPLAIN의 Extra에 Using index로 표시된다. SELECT *는 커버링을 깨는 가장 흔한 원인이다.

7. 인덱스를 타지 못하는 경우

경우예이유
컬럼을 가공WHERE YEAR(created_at) = 2025정렬된 원래 값과 비교할 수 없음
암묵적 형변환문자열 컬럼에 WHERE phone = 01012345678컬럼 쪽이 변환되어 가공한 것과 같음
앞부분 와일드카드WHERE name LIKE '%고기'시작 값을 몰라 정렬을 이용 못 함
부정 조건WHERE status != 'DONE'대부분의 행이 해당돼 풀 스캔이 나음
OR 조건WHERE a = 1 OR b = 2양쪽 모두 인덱스가 없으면 풀 스캔
낮은 선택도WHERE gender = 'M'걸러도 절반이 남아 인덱스 이점이 없음

공통 원리는 "인덱스는 가공하지 않은 원래 값의 정렬"이라는 것이다.

8. 인덱스를 걸면 좋은 컬럼

  1. WHERE, JOIN의 ON, ORDER BY, GROUP BY에 자주 쓰이는 컬럼
  2. 카디널리티가 높은 컬럼 (회원 ID, 이메일, 주문번호)
  3. 수정이 잦지 않은 컬럼

9. EXPLAIN 읽는 법

항목볼 것
typeconst, eq_ref, ref, range는 양호 / index(인덱스 전체 스캔), ALL(풀 스캔)은 주의
key실제로 사용한 인덱스 (NULL이면 인덱스 미사용)
rows읽을 것으로 예상한 행 수
ExtraUsing index(커버링, 좋음), Using filesort·Using temporary(별도 정렬·임시 테이블, 주의)

10. 실제로 어떻게 적용되나

  1. 회원의 최근 주문 목록: WHERE member_id = ? ORDER BY ordered_at DESC LIMIT 20 쿼리에는 (member_id, ordered_at) 복합 인덱스를 건다. 등호 컬럼이 앞, 정렬 컬럼이 뒤라서 인덱스 순서대로 20개만 읽고 끝나고 filesort가 사라진다.
  2. 날짜 검색: DATE(created_at) = '2025-12-09' 대신 created_at >= '2025-12-09' AND created_at < '2025-12-10'으로 바꾸면 컬럼을 가공하지 않으므로 인덱스를 탄다.
  3. 목록 화면 최적화: 목록에 필요한 몇 개 컬럼만 SELECT하고 그 컬럼들을 인덱스에 포함시켜 커버링 인덱스로 만든다.
  4. 중복 인덱스 정리: (a)와 (a, b)가 둘 다 있으면 (a)는 중복이다. (a, b)가 a 단독 조건도 처리하므로 하나를 지워 쓰기 비용을 줄인다.

11. 면접 질문으로 정리

  1. Q. 인덱스란? 장단점은? 컬럼 값을 정렬해 둔 별도 자료구조로 검색·정렬 속도를 높인다. 대신 저장 공간이 들고, 데이터 변경 시 인덱스도 갱신해야 해서 쓰기 성능이 떨어진다.
  2. Q. 왜 B+Tree를 쓰나? 노드당 여러 키를 저장해 트리 높이가 낮아 디스크 접근이 적고, 리프 노드가 연결돼 있어 범위 검색에 유리하기 때문이다. 해시는 등호 검색은 빠르지만 범위 검색과 정렬을 지원하지 못한다.
  3. Q. 클러스터드 인덱스와 논클러스터드 인덱스의 차이는? 클러스터드 인덱스는 테이블 데이터 자체가 그 순서로 저장되어 테이블당 하나이고, 논클러스터드 인덱스는 별도 구조로 여러 개 만들 수 있으며 실제 행을 찾으려면 한 번 더 탐색한다.
  4. Q. 복합 인덱스 컬럼 순서는 어떻게 정하나? 최좌측 접두사 규칙 때문에 자주 쓰는 등호 조건 컬럼을 앞에, 범위·정렬 조건 컬럼을 뒤에 둔다.
Gunmo Lee
DB 인덱스 개념 정리: B+Tree, 클러스터드 vs 논클러스터드, 복합 인덱스 | Gunmo's Dev Life