한양과학기술고등학교

에듀테크 프로젝트 대시보드

데이터베이스 인덱스

100만 행에서 1건을 순식간에 찾는 법

인덱스는 책 뒤의 「찾아보기」와 같다. 정렬된 목록과 페이지 번호(행 위치)를 미리 만들어 두면, 처음부터 끝까지 훑지 않고 이진 탐색으로 바로 도달한다. 실제 DB는 이 구조를 B-Tree로 디스크에 저장한다.

인덱스 없음 — Full Table Scan

SELECT * FROM users WHERE user_id = ?

읽은 행 수0

인덱스 사용 — Index Seek

idx_user_id (user_id ASC) → 이진 탐색

비교 횟수0
위에서 user_id를 고르고 버튼을 누르면, 두 방식이 각각 몇 번 만에 찾는지 비교됩니다.

데이터가 커지면? — 필요한 조회 횟수 (평균)

빨라지는 것

WHERE · JOIN 조건 컬럼 조회, ORDER BY 정렬, 범위 검색(BETWEEN). O(n) 스캔이 O(log n) 탐색으로.

치르는 비용

INSERT / UPDATE / DELETE 할 때마다 인덱스도 다시 정렬·갱신 → 쓰기가 느려짐. 인덱스 자체도 디스크 공간을 차지한다.

효과 없을 때

값 종류가 적은 컬럼(성별 등 낮은 카디널리티), LIKE '%..', 컬럼에 함수 적용(WHERE YEAR(d)=..), 아주 작은 테이블.

-- 인덱스 만들기 / 확인 / 실행계획 보기 CREATE INDEX idx_user_id ON users (user_id); EXPLAIN SELECT * FROM users WHERE user_id = 4711; -- type: ref key: idx_user_id rows: 1 ← 인덱스 탐색 -- type: ALL key: NULL rows: 1000000 ← 풀 스캔(느림) -- 복합 인덱스: 왼쪽 컬럼부터 순서대로 써야 효과 (왼쪽 접두사 규칙) CREATE INDEX idx_dept_name ON students (dept, name); -- WHERE dept=? AND name=? ✅ 사용 | WHERE name=? ❌ 미사용

인덱스는 "정렬된 사본 + 행 포인터" 일 뿐, 원본 데이터를 대신하지 않는다. 그래서 조건에 안 맞는 컬럼까지 필요하면 인덱스에서 행 위치를 얻은 뒤 원본 테이블을 다시 읽는다(lookup). 조회하는 모든 컬럼이 인덱스에 포함되면 원본을 안 읽어도 되는데, 이를 커버링 인덱스라 한다.

💡 왜 이진 탐색 트리가 아니라 하필 "B-Tree"일까?

DB 인덱스는 대부분 메모리가 아니라 디스크에 있고, 디스크는 한 번에 한 블록(예: 16KB)씩 읽는다. 이진 트리는 노드마다 자식이 2개뿐이라 깊이가 깊어져 디스크 접근이 많아진다. B-Tree는 한 노드에 수백 개의 키를 담아 자식이 수백 갈래 → 깊이가 3~4단계로 얕아진다. 100만 행도 디스크 읽기 3~4번이면 도달한다. "넓고 얕게"가 핵심.

💡 클러스터형 인덱스 vs 보조 인덱스

클러스터형(Clustered): 테이블의 실제 행이 인덱스 키 순서대로 저장된다. 테이블당 1개(보통 기본키). 보조(Secondary) 인덱스: 별도로 만든 인덱스로, 리프에 "행의 위치(또는 기본키 값)"를 담는다. 그래서 보조 인덱스로 찾으면 → 기본키를 얻고 → 클러스터형 인덱스를 한 번 더 타는 경우가 많다.