429. 인덱스 레인지 스캔 (Index Range Scan)

⚠️ 이 문서는 데이터베이스 검색 시 가장 흔하고 이상적으로 동작하는 방식인, **수문장(Root)부터 바닥(Leaf)까지 한 번 수직으로 파고든 다음, 조건에 맞는 데이터가 끝날 때까지 잎사귀들을 타고 옆으로 쭉 긁어오는 '인덱스 레인지 스캔'**을 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: B+Tree(422번 문서) 인덱스의 핵심 장점인 '리프 노드의 순차적 연결(Linked List)'을 100% 활용하는 검색 방식이다.
  2. 작동 방식: 수직적 탐색으로 스캔의 **시작점(Start Point)**을 찾고, 수평적 탐색으로 스캔의 **종료점(Stop Point)**까지 쭈욱 읽어 나간다.
  3. 가치: <, >, BETWEEN, LIKE '김%' 같이 범위(Range)로 데이터를 찾을 때, 쓸데없는 데이터를 건너뛰고 정확히 필요한 구간만 스캔할 수 있게 해주는 최고의 최적화 기법이다.

Ⅰ. 개요: 책갈피부터 3페이지만 읽기 (Context & Necessity)

"나이가 20살에서 25살 사이인 회원을 찾아줘!"

  • 테이블 풀 스캔 (428번 문서): 1쪽부터 끝까지 다 읽으며 20~25살을 솎아낸다. (멍청함)
  • 인덱스 유니크 스캔 (Index Unique Scan): 딱 1개의 점(Point)만 찾는 방식이라, 20살 하나 찾고 멈춘다. 범위 검색에는 못 쓴다.

인덱스 레인지 스캔은 이 둘을 섞어놓은 완벽한 타협점이다.

  1. "20살"이 있는 페이지(책갈피)를 순식간에 찾아서 펼친다. (수직적 탐색)
  2. 거기서부터 눈동자를 움직여서 "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줄 비유 설명

  1. 레인지 스캔은 출석부에서 '김씨' 친구들을 다 찾는 것과 같아요.
  2. 출석부는 가나다순으로 되어 있으니까, 'ㄱ' 페이지를 착 펴서 첫 번째 '김'씨를 찾죠 (시작점 찾기).
  3. 거기서부터 아래로 줄을 쭉 그으면서 '나'씨가 나오기 전까지 이름들을 몽땅 베껴오면 끝이랍니다! (수평으로 쭉 긁어오기)