
안녕하세요! SSAFY 15기 유상은 기자입니다.
오늘은 데이터베이스 조회 성능 향상의 핵심 기술인
인덱스(Index)가 무엇이고, 왜 필요한지에 대해 정리해보려고 합니다.
1. 인덱스란 무엇인가?
인덱스(Index)는 테이블의 데이터를 빠르게 찾기 위해 만들어 둔 별도의 정렬된 자료구조다.
책의 색인을 떠올리면 이해하기 쉽다.
책에서 '운영체제'라는 단어를 찾는 상황을 생각해 보자.
방법은 두 가지가 있다.
방법 1
1페이지부터 끝까지 전부 읽는다.
방법 2
책 뒤쪽 색인(Index)에서
'운영체제 → 153페이지'를 확인한 뒤
해당 페이지로 바로 이동한다.
DB도 동일하다.
| 책 | DB |
| 본문 | 테이블 |
| 색인 | 인덱스 |
| 페이지 번호 | ROWID (데이터 위치 정보) |
2. 왜 인덱스가 필요할까?
Full Table Scan
아래 쿼리를 생각해 보자.
SELECT *
FROM MEMBER
WHERE NAME = '홍길동';
인덱스가 없다면 DB는 '홍길동'이 어디에 있는지 알 수 없다.
처음부터 끝까지 하나씩 확인해야 한다.
이처럼 테이블 전체를 읽는 방식을 Full Table Scan이라고 한다.
SQL이 느린 진짜 이유
많은 사람들이 SQL이 느린 이유를 복잡한 계산 때문이라고 생각한다.
하지만 실제 성능 문제는 대부분 데이터를 읽어오는 과정에서 발생한다.
NAME = '홍길동' 인지 비교하는 연산 자체는 CPU가 매우 빠르게 처리한다.
문제는 비교하기 위해 데이터를 디스크에서 가져오는 과정이다.
따라서 SQL 튜닝은 하나의 목표를 가진다.
📍 불필요한 데이터를 최대한 적게 읽는 것
그리고 인덱스는 바로 이 문제를 해결하기 위해 만들어진 기술이다.
읽어야 할 블록의 범위를 줄여주는 것이 인덱스의 역할이다.
3. 인덱스는 어떻게 빠른가?
인덱스가 빠른 이유는 "전체를 읽지 않기 때문"이다.
그렇다면 실제로 인덱스는 어떤 방식으로 데이터를 찾을까?
B+Tree 구조
대부분의 DB는 인덱스를 B+Tree 구조로 저장한다.
Root Block
↓
Branch Block
↓
Leaf Block ↔ Leaf Block ↔ Leaf Block
각 계층의 역할은 다음과 같다.
- Root / Branch : 하위 블록으로 내려가기 위한 포인터만 보유
- Leaf : 실제 인덱스 키 값 + ROWID 저장. Leaf 블록끼리는 양방향 LinkedList로 연결
여기서 핵심은 두 가지다.
- 인덱스는 항상 정렬된 상태로 유지된다.
- Leaf 블록은 서로 연결되어 있다.
이 두 가지 특성이 빠른 탐색을 가능하게 한다.
탐색 단계
1단계 : 수직적 탐색 (시작지점 찾기)
Root → Branch → Leaf 순서로 내려가며,
조건을 만족하는 첫 번째 레코드가 있는 Leaf 블록을 찾는다.
정렬된 구조 덕분에 전체를 뒤지지 않고 범위를 절반씩 좁혀가며 내려갈 수 있다.
2단계 : 수평적 탐색 (데이터 수집)
시작지점을 찾은 뒤, Leaf 블록을 옆으로 읽어나간다.
조건을 벗어나는 값이 나오면 즉시 종료한다.
이 과정에서 ROWID를 수집하고, 해당 ROWID로 테이블 블록에 직접 접근한다.
ROWID란?
ROWID는 실제 데이터가 저장된 위치 정보다.
인덱스는 데이터를 직접 저장하는 것이 아니라
데이터가 저장된 위치를 알고 있다.
따라서 인덱스는 ROWID를 이용해 실제 테이블 데이터에 접근한다.
수직 탐색 Root → Branch → Leaf (시작지점 찾기)
↓
수평 탐색 Leaf → Leaf → Leaf (ROWID 수집)
↓
테이블 접근 ROWID → 테이블 블록 (실제 데이터 읽기)
인덱스가 빠른 이유는 "전체를 안 읽어도 되기 때문"이다.
수직 탐색으로 시작지점을 바로 찾고, 수평 탐색으로 필요한 범위만 읽는다.
4. 인덱스의 트레이드오프
그렇다고 인덱스가 무조건 좋은 건 아니다.
쓰기 비용
인덱스는 테이블과 별도로 존재하는 자료구조다.
INSERT / UPDATE / DELETE가 발생하면
테이블뿐만 아니라 인덱스도 함께 갱신된다.
인덱스가 많을수록 쓰기 성능이 낮아진다.
Full Scan이 더 빠를 때도 있다
먼저 두 가지 I/O 방식을 구분할 필요가 있다.
Sequential I/O (순차)
연속된 블록을 순서대로 한 번에 읽는 방식
1 → 2 → 3 → 4 → 5
Random I/O (랜덤)
필요한 블록을 여기저기 찾아가며 읽는 방식
3 → 101 → 9 → 220 → 15
인덱스를 사용하면 ROWID로 테이블 블록에 접근할 때마다 매번 랜덤 I/O가 발생한다.
반면 Full Table Scan은 블록을 순서대로 읽는 Sequential I/O다.
| 상황 | 유리한 방식 | 이유 |
| 조회 건수 적음 | 인덱스 스캔 | 필요한 위치만 빠르게 접근 |
| 조회 건수 많음 | Full Table Scan | Sequential I/O가 랜덤 I/O보다 효율적 |
따라서 인덱스가 있어도 조회 건수가 충분히 많으면 DB가 Full Scan을 선택할 수 있다.
실제로 그 방법이 더 빠르기 때문이다.
참고 자료: 친절한 SQL 튜닝
📢 SSAFY 공식 채널 안내
📸 기자단 SNS 바로가기 →
SSAFY 기자단의 다양한 콘텐츠(SSAFY 생활, IT 지식, 취업 및 현직자 이야기)는 아래 채널에서 확인하실 수 있습니다.
🎥 SSAFY 유튜브 | 헬로싸피(싸피티비) 바로가기 →
AI, 취업, 인생 꿀팁까지! 라이브 방송과 싸피티비 콘텐츠가 주기적으로 방송됩니다.
🌐 SSAFY 공식 홈페이지 바로가기 →
SSAFY의 공식 홈페이지를 통해 주요 일정 및 내용을 확인해 주세요.
