429. 인덱스 레인지 스캔 (Index Range Scan)
⚠️ 이 문서는 데이터베이스 검색 시 가장 흔하고 이상적으로 동작하는 방식인, **수문장(Root)부터 바닥(Leaf)까지 한 번 수직으로 파고든 다음, 조건에 맞는 데이터가 끝날 때까지 잎사귀들을 타고 옆으로 쭉 긁어오는 '인덱스 레인지 스캔'**을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: B+Tree(422번 문서) 인덱스의 핵심 장점인 '리프 노드의 순차적 연결(Linked List)'을 100% 활용하는 검색 방식이다.
- 작동 방식: 수직적 탐색으로 스캔의 **시작점(Start Point)**을 찾고, 수평적 탐색으로 스캔의 **종료점(Stop Point)**까지 쭈욱 읽어 나간다.
- 가치:
<,>,BETWEEN,LIKE '김%'같이 범위(Range)로 데이터를 찾을 때, 쓸데없는 데이터를 건너뛰고 정확히 필요한 구간만 스캔할 수 있게 해주는 최고의 최적화 기법이다.
Ⅰ. 개요: 책갈피부터 3페이지만 읽기 (Context & Necessity)
"나이가 20살에서 25살 사이인 회원을 찾아줘!"
- 테이블 풀 스캔 (428번 문서): 1쪽부터 끝까지 다 읽으며 20~25살을 솎아낸다. (멍청함)
- 인덱스 유니크 스캔 (Index Unique Scan): 딱 1개의 점(Point)만 찾는 방식이라,
20살하나 찾고 멈춘다. 범위 검색에는 못 쓴다.
인덱스 레인지 스캔은 이 둘을 섞어놓은 완벽한 타협점이다.
- "20살"이 있는 페이지(책갈피)를 순식간에 찾아서 펼친다. (수직적 탐색)
- 거기서부터 눈동자를 움직여서 "26살"이라는 글자가 보일 때까지 계속 다음 페이지로 넘기며 읽는다. (수평적 탐색)
📢 섹션 요약 비유: 레인지 스캔은 **'사전에서 Apple부터 Banana까지 찾기'**와 같습니다. 사전을 촥 펼쳐서 'Apple'이 있는 페이지를 한 번에 찾고, 그다음부터는 굳이 알파벳 인덱스를 다시 찾을 필요 없이 'Banana'가 나올 때까지 종이만 계속 옆으로 넘기는 방식입니다.
Ⅱ. 레인지 스캔의 조건과 위험성 ★
레인지 스캔이 작동하려면 쿼리를 짤 때 아주 중요한 규칙들을 지켜야 한다.
1. 선두 컬럼의 가공 금지
WHERE age * 10 > 200(❌ 인덱스 안 탐)WHERE age > 20(🟢 인덱스 레인지 스캔)- 인덱스는
age값으로 정렬되어 있다. 컬럼 자체에 곱하기나 함수를 씌우면 책갈피가 다 꼬여버려서 처음부터 다 뒤져야 한다.
2. LIKE 검색은 '앞쪽'이 고정되어야 한다
WHERE name LIKE '%철수'(❌ 인덱스 안 탐)WHERE name LIKE '김%'(🟢 인덱스 레인지 스캔)- 뒷글자(
철수)는 어디서 시작할지 시작점(Start Point)을 알 수 없어서 풀 스캔을 한다. 앞글자(김)를 고정해 줘야 'ㄱ' 챕터부터 시작해서 'ㄴ'이 나오기 전까지 쭉 읽을 수 있다.
3. 랜덤 I/O (Key Lookup)의 폭발 조심
- 인덱스를 타고 20~25살 10만 명을 찾았다 치자.
- 근데 인덱스에 없는 '주소' 컬럼을 쿼리에서
SELECT했다면? - DB는 10만 명의 진짜 주소를 찾기 위해 하드디스크로 **10만 번의 랜덤 점프(Key Lookup, 423번 문서 참조)**를 뛰어야 한다. 이때 서버가 터진다.
- 차라리 이럴 땐 옵티마이저가 인덱스를 포기하고 '테이블 풀 스캔'을 타는 게 훨씬 빠르다.
Ⅲ. Index Full Scan과의 비교
이름은 비슷하지만 하늘과 땅 차이다.
- Index Range Scan (레인지 스캔): 시작점과 끝점이 명확하다. (예: 20살~25살) 인덱스의 **'일부'**만 읽으므로 아주 빠르다.
- Index Full Scan (인덱스 풀 스캔): 시작점과 끝점이 없다. 디스크의 본문 데이터를 다 읽는 것보단 낫지만, 어쨌든 **'인덱스 책자 전체'**를 처음부터 끝까지 다 읽어보는 것이다. 별로 좋은 신호가 아니다.
┌──────────────────────────────────────────────────────────────┐
│ 인덱스 레인지 스캔 (Index Range Scan) 작동 구조 시각화 │
├──────────────────────────────────────────────────────────────┤
│ │
│ Q. "나이가 20 이상, 30 이하인 사람을 찾아라!" │
│ │
│ [ 🎋 루트/브랜치 노드 ] ──(수직적 탐색)──▶ "20을 찾아라!" │
│ │ │
│ ▼ │
│ [ 🍃 리프 노드 (정렬됨) ] │
│ [10, 15] ──▶ [20, 25] ──▶ [28, 30] ──▶ [35, 40] │
│ ▲ (시작점) ▲ (종료점) │
│ │ │ │
│ └─────(수평적 스캔)──────┘ │
│ │
│ ★ 특징: 20을 찾은 뒤부터는 위로 안 올라가고 옆으로만 쭈~욱 긁어온다! │
└──────────────────────────────────────────────────────────────┘
Ⅳ. 결론
"조건절의 모양이 인덱스의 운명을 결정한다."
인덱스 레인지 스캔은 관계형 데이터베이스가 수십 년 동안 사랑받아 온 가장 강력하고 효율적인 무기다. 하지만 개발자가 LIKE '%키워드%'처럼 양방향 와일드카드를 남발하거나, 암시적 형변환(문자를 숫자로 억지로 비교)을 일으키면, 애써 만들어둔 인덱스는 잠들어버리고 쿼리 속도는 나락으로 떨어진다. 내가 짠 쿼리가 수직적 탐색으로 명확한 '시작점'을 찍을 수 있는지 고민하는 것, 그것이 쿼리 튜닝의 시작이자 끝이다.
📌 관련 개념 맵
- 전제 조건: B+Tree 인덱스 (422번 문서)
- 비교 방식: Index Unique Scan (단건 검색), Index Full Scan, Table Full Scan (428번 문서)
- 발생 조건:
<,<=,>,>=,BETWEEN,LIKE '검색어%' - 주의점: 결합 인덱스일 경우, 앞쪽 컬럼이 누락되면 레인지 스캔을 탈 수 없음 (427번 문서)
👶 어린이를 위한 3줄 비유 설명
- 레인지 스캔은 출석부에서 '김씨' 친구들을 다 찾는 것과 같아요.
- 출석부는 가나다순으로 되어 있으니까, 'ㄱ' 페이지를 착 펴서 첫 번째 '김'씨를 찾죠 (시작점 찾기).
- 거기서부터 아래로 줄을 쭉 그으면서 '나'씨가 나오기 전까지 이름들을 몽땅 베껴오면 끝이랍니다! (수평으로 쭉 긁어오기)