예전에 면접에서 인덱스 관련 질문을 받았다.
👩 : "인덱스가 뭔가요?"
🥔 : "검색 속도를 높이기 위한 자료구조입니다."
👩 : "그럼 MySQL은 어떤 인덱스 구조를 쓰는지 아시나요? B+Tree 아세요?"
🥔 : 음...
이후 질문에는 제대로 대답하지 못했다.
그 순간, 단순히 개념만 외우는 것만으로는 부족하다는 걸 느꼈다.
‘빠르다’는 건 알겠는데, 정확히 어떻게 동작하고 왜 그렇게 빠른지에 대해서는 설명할 수 없었다.
그래서 이번 글에서는 인덱스의 개념부터 MySQL이 사용하는 B+Tree 구조,
그리고 인덱스를 언제 쓰는 게 효과적이고, 언제 오히려 비효율적인지까지 정리해보려 한다.
실제로 인덱스는 성능 최적화의 핵심이지만, 잘못 쓰면 발목을 잡기도 한다.
면접 질문 하나에서 시작된 고민이지만, 지금은 실무에서도 꼭 짚고 넘어가야 할 주제라고 생각한다.
인덱스(Index)는 데이터베이스에서 검색 속도를 빠르게 하기 위해 사용하는 자료 구조다.
쉽게 말해, 책의 목차처럼 원하는 데이터를 빠르게 찾기 위해 미리 만들어둔 데이터의 정렬된 목록이다.
WHERE : 조건 필터링JOIN : 연결 키 기반 row 탐색ORDER BY : 이미 정렬된 인덱스 기반 순회GROUP BY : 인덱스 기반 그룹핑 성능 향상UNIQUE, PRIMARY KEY 는 인덱스를 사용하여 값 중복을 빠르게 판별함때문에 인덱스는 대량의 데이터가 있는 시스템에서 특히 중요한 역할을 한다. 주로 반복적으로 조회되는 컬럼에 인덱스를 적용하는 것이 효과적이다.
SELECT * FROM USER WHERE grade = 2;
총 2,000명의 학생이 저장된 USER 테이블에서, 2학년 학생만 조회하는 쿼리라고 가정해보자.


MySQL(InnoDB)의 인덱스는 내부적으로 B+Tree라는 구조를 기반으로 구현되어 있다.
이는 B-Tree의 개념을 확장한 형태이며 데이터를 정렬된 상태로 저장하고, 범위 조회나 순차 탐색에 최적화된 구조다.
B-Tree 와 B+Tree에 대해서 자세히 알아보자.
일반적인 트리부터 살펴보자.
|
|
| 일반적인 Tree | 최악의 Tree 구조 |
이러한 성능 저하 문제를 해결하기 위해 등장한 것이 바로
균형 잡힌 트리 구조인 B-Tree이다.B-Tree 구조
아래는 3차(차수 3) B-Tree가 생성되는 과정을 보여준다.
삽입 순서는 다음과 같다
1 → 15 → 2 → 5 → 30
이러한 key 값이 순서대로 삽입될 때, B-Tree는 내부적으로 균형(Balanced)을 유지하면서 분할(Split)과 승격(Promotion)을 수행한다.
삽입 과정을 그려보면...


이렇게 B-Tree는 삽입이 반복되더라도 트리 전체의 균형을 유지하며 검색 성능을 항상 O(log N)으로 보장할 수 있도록 구조가 자동으로 조정된다.
B-Tree는 데이터의 검색에는 유리하다. 하지만 모든 데이터를 풀 스캔하려면 모든 트리를 전부 방문해야 한다.
이러한 단점을 개선한 것이 B+Tree 이다.

그럼 이 구조로 어떤 이점을 얻을까?
트리 높이 감소 (검색 속도 향상)
범위 검색 및 순차 접근에 최적화
BETWEEN, ORDER BY, LIMIT 쿼리에 매우 효율적효율적인 메모리 및 저장 공간 사용
| 구분 | B-Tree | B+Tree |
|---|---|---|
| 데이터 저장 위치 | 내부 노드와 리프 노드에 모두 저장 | 리프 노드에만 저장 |
| 내부 노드 구성 | 키 + 데이터 | 키 + 포인터만 |
| 리프 노드 연결 | 연결되어 있지 않음 | Linked List로 연결됨 |
| 트리 높이 | 상대적으로 깊어질 수 있음 | 더 많은 키 저장 가능 → 트리 높이 낮음 |
| 검색 효율 | 단일 키 검색은 빠름 | 단일 검색 + 범위 검색 모두 효율적 |
| 범위 검색 성능 | 트리 재탐색 반복 필요 → 비효율적 | 한 번 탐색 후 리프 노드 순차 접근 가능 |
| 중복 키 저장 | 없음 | 가능 (브랜치 노드에 중복 키 존재) |
| 사용 예시 | 메모리 기반 트리 (TreeMap 등) | 디스크 기반 DB 인덱스 (MySQL, Oracle 등) |
| 인덱스 구조로의 적합성 | 디스크 I/O 최적화에 불리함 | 디스크 I/O 최소화에 최적화 |
인덱스가 그렇게 좋은데, 왜 아무 컬럼에나 다 쓰지 않을까?
SELECT, WHERE, ORDER BY, JOIN, GROUP BY 등에 모두 활용 가능하다.✅ 읽기는 빠르게!
❌ 하지만 쓰기와 저장에는 비용이 든다.
쓰기(INSERT/UPDATE/DELETE) 성능 저하
디스크 공간 추가 사용
잘못 만든 인덱스는 오히려 성능 악화
조회(SELECT) 비중이 높은 테이블
검색 조건으로 자주 사용하는 컬럼
WHERE user_id = ?WHERE created_at BETWEEN ? AND ?정렬이나 그룹 연산이 자주 발생하는 컬럼
ORDER BY, GROUP BY, DISTINCT 등에 포함되는 컬럼조인(JOIN) 조건으로 사용되는 컬럼
예: 유저가 많은 서비스의
user_id,created_at등
→ Full Table Scan이 더 효율적인 경우
작은 테이블 (예: 몇 백 건 이하)
선택도가 낮은 컬럼 (중복 많음)
gender, status = 'ACTIVE' 같이 값이 거의 다 같은 경우계산이나 함수가 적용된 WHERE 조건
WHERE YEAR(created_at) = 2024LIKE '%abc'
💡 "인덱스를 만든다고 다 쓰는 게 아니다."
→ 결국 선택도(필터링 비율)와 사용 패턴이 핵심이다.
인덱스는 "읽기 성능을 위한 강력한 도구"지만 쓰기 성능과 저장 효율을 희생하기 때문에 전략적으로 적용해야 한다.
실제로는 쿼리 패턴, 테이블 크기, 컬럼 특성 등을 고려해 정밀하게 설계하는 것이 중요하다.