인덱스 (Index)

한눈에 보기

인덱스는 컬럼 값과 저장 위치를 별도 자료구조로 정리해 검색 속도를 높이는 장치다. B+Tree 동작 원리, 클러스터드와 논클러스터드 비교, 복합 인덱스와 커버링 인덱스, 설계 시 주의점을 다룬다.

최종 업데이트 2026.09.17

인덱스(Index)는 데이터베이스 테이블의 검색 속도를 높이기 위해 특정 컬럼의 값과 그 값이 저장된 위치를 별도의 자료구조로 정리해 둔 것이다. 책의 색인처럼 원하는 데이터를 전체 테이블을 훑지 않고 바로 찾아갈 수 있게 해 주지만, 인덱스 자체를 저장하고 갱신하는 비용이 추가되므로 조회와 쓰기 사이의 트레이드오프를 이해하고 사용해야 한다.

인덱스가 필요한 이유

인덱스가 없는 컬럼을 조건으로 검색하면 데이터베이스는 테이블의 모든 행을 처음부터 끝까지 읽는 풀 테이블 스캔(Full Table Scan)을 수행한다. 행이 수천 건일 때는 문제가 없지만 수백만, 수억 건으로 늘어나면 조회 시간이 감당할 수 없이 길어진다. 인덱스는 대상 컬럼의 값을 정렬된 상태로 유지하기 때문에, 이진 탐색과 유사한 방식으로 원하는 값을 로그 시간에 찾을 수 있다. 대신 데이터가 삽입, 수정, 삭제될 때마다 인덱스도 함께 갱신해야 하므로 쓰기 성능은 다소 희생된다.

B-Tree 인덱스의 동작 방식

대부분의 관계형 데이터베이스가 기본으로 사용하는 구조는 비트리(B-Tree), 정확히는 그 변형인 비플러스트리(B+Tree)다. 루트 노드에서 시작해 브랜치 노드를 거쳐 리프 노드에 도달하는 균형 잡힌 트리 구조로, 어떤 값을 찾더라도 디스크 접근 횟수가 트리의 높이만큼으로 일정하다. 비플러스트리는 실제 데이터의 위치 정보를 리프 노드에만 저장하고, 리프 노드끼리 연결 리스트로 이어져 있어 범위 검색(Range Scan)에 특히 유리하다. 같음 비교뿐 아니라 부등호 조건, BETWEEN, 정렬, LIKE의 전방 일치 검색까지 인덱스를 활용할 수 있는 이유가 여기에 있다.

반면 해시 인덱스(Hash Index)는 해시 함수로 값을 버킷에 매핑하므로 동등 비교는 매우 빠르지만 범위 검색과 정렬에는 쓸 수 없다. 그래서 메모리 기반 테이블이나 일부 특수한 용도에 제한적으로 쓰인다.

클러스터드 인덱스와 논클러스터드 인덱스

인덱스는 실제 데이터의 물리적 정렬 여부에 따라 두 가지로 나뉜다.

구분클러스터드 인덱스(Clustered Index)논클러스터드 인덱스(Non-Clustered Index)
데이터 저장리프 노드가 실제 데이터 페이지이며 키 순서대로 저장된다리프 노드에 데이터 위치 정보(포인터 또는 기본키)만 저장된다
테이블당 개수1개만 가능하다여러 개 만들 수 있다
조회 성능범위 조회에 매우 빠르다인덱스 탐색 후 실제 데이터를 한 번 더 찾아가야 한다
대표 예MySQL InnoDB의 기본키일반적으로 생성하는 보조 인덱스

MySQL의 InnoDB 스토리지 엔진에서는 보조 인덱스의 리프 노드가 행의 물리 주소 대신 기본키 값을 담는다. 따라서 보조 인덱스로 검색하면 먼저 보조 인덱스에서 기본키를 얻고, 다시 클러스터드 인덱스를 타고 실제 행을 찾는 두 단계 탐색이 일어난다. 기본키가 길수록 모든 보조 인덱스가 커지므로 기본키는 가급적 짧게 유지하는 것이 좋다.

복합 인덱스와 커버링 인덱스

두 개 이상의 컬럼을 묶어 만든 것을 복합 인덱스(Composite Index)라고 한다. 복합 인덱스는 선언한 컬럼 순서대로 정렬되므로 첫 번째 컬럼을 조건에 사용하지 않으면 인덱스를 제대로 타지 못한다. 이를 최좌측 접두어(Leftmost Prefix) 규칙이라고 부르며, 어떤 컬럼을 앞에 둘지가 설계의 핵심이다.

또한 쿼리가 요구하는 모든 컬럼이 인덱스 안에 포함되어 있으면 실제 테이블에 접근하지 않고 인덱스만 읽어서 결과를 반환할 수 있는데, 이를 커버링 인덱스(Covering Index)라고 한다. 디스크 접근이 크게 줄어 성능 최적화에 자주 활용된다.

-- 복합 인덱스 생성
CREATE INDEX idx_orders_user_date ON orders (user_id, created_at);

-- 인덱스를 활용하는 쿼리 (user_id 선행 조건 포함)
SELECT id, created_at
FROM orders
WHERE user_id = 42 AND created_at >= '2026-01-01';

-- 실행 계획 확인
EXPLAIN SELECT * FROM orders WHERE user_id = 42;

인덱스 설계 시 고려할 점

인덱스는 카디널리티(Cardinality), 즉 중복이 적고 값이 다양한 컬럼에 걸어야 효과가 크다. 성별처럼 값의 종류가 적은 컬럼은 인덱스를 타더라도 걸러지는 행이 적어 이득이 없다. WHERE, JOIN, ORDER BY에 자주 등장하는 컬럼이 우선 후보가 된다.

반대로 쓰기가 빈번한 테이블에 인덱스를 과도하게 만들면 INSERT, UPDATE, DELETE마다 모든 인덱스를 갱신해야 해서 성능이 떨어지고, 페이지 분할로 인한 단편화도 발생한다. 또한 인덱스 컬럼에 함수를 적용하거나 타입이 다른 값과 비교하면 인덱스를 사용하지 못하는 점, 통계 정보가 낡으면 옵티마이저(Optimizer)가 비효율적인 실행 계획을 선택할 수 있다는 점도 함께 기억해야 한다.

면접에서 이렇게 물어본다

  • 인덱스를 사용하면 조회가 빨라지는 원리를 자료구조 관점에서 설명해 보라.
  • 비플러스트리가 해시 인덱스보다 데이터베이스 인덱스로 널리 쓰이는 이유는 무엇인가.
  • 클러스터드 인덱스와 논클러스터드 인덱스의 차이, 그리고 InnoDB에서 보조 인덱스 조회가 두 단계로 일어나는 이유를 설명해 보라.
  • 복합 인덱스에서 컬럼 순서가 중요한 이유와 커버링 인덱스가 빠른 이유는 무엇인가.
  • 인덱스를 걸었는데도 쿼리가 인덱스를 타지 않는 경우를 예로 들어 보라.
데이터베이스 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기