09. 읞덱싱

09. 읞덱싱

읎전: 질의 처늬와 최적화 | 닀음: 튞랜잭션 읎론


학습 목표

  • 읞덱싱읎 데읎터베읎슀 성능에 쀑요한 읎유 읎핎
  • 정렬된 읞덱슀(Ordered Indices) 구분: 죌 읞덱슀(Primary), 큎러슀터링(Clustering), 볎조 읞덱슀(Secondary)
  • B-Tree와 B+Tree 구조, 연산 및 복잡도 마슀터
  • 핎시 êž°ë°˜ 읞덱싱 Ʞ법: 정적(Static), 확장가능(Extendible), 선형 핎싱(Linear Hashing)
  • 비튞맵 읞덱슀(Bitmap Indices)와 닀찚원 읞덱슀 구조 읎핎
  • 싀묎에서 읞덱슀 섀계 지칚 적용

1. 읞덱싱읎 쀑요한 읎유

Ʞ볞 묞제

100만 개의 행을 가진 employees 테읎랔읎 디슀크에 저장되얎 있닀고 가정하자. 각 디슀크 랔록은 10개의 행을 닎윌므로, 테읎랔은 10만 개의 랔록을 찚지한닀. ID로 직원 한 명을 찟윌렀멎:

읞덱슀 없읎:  몚든 10만 개 랔록 슀캔  →  O(n) 디슀크 I/O
읞덱슀 사용:  읞덱슀 포읞터 따띌가Ʞ  →  O(log n) 디슀크 I/O

10만 랔록의 겜우: - 순찚 슀캔: 최대 10만 번의 랔록 읜Ʞ - B+Tree 읞덱슀 (ë¶„êž° 계수 200): 대략 log_200(1,000,000) ≈ 3 랔록 읜Ʞ

읎는 33,000배의 I/O 비용 개선읎닀.

순찚 vs. 읞덱슀 ì ‘ê·Œ

순찚 ì ‘ê·Œ (테읎랔 슀캔):
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ Blk1 │ Blk2 │ Blk3 │ Blk4 │ Blk5 │ ...  │ BlkN │
└──────┮──────┮──────┮──────┮──────┮──────┮──────┘
  ↑ 몚든 랔록을 순찚적윌로 읜음

읞덱슀 ì ‘ê·Œ:
┌─────────────────┐
│   읞덱슀 룚튞    │  ← 1 I/O
│  [50│100│150]    │
└──┬──────┬──────┬─┘
   ↓      ↓      ↓
 ┌────┐ ┌────┐ ┌────┐
 │늬프│ │늬프│ │늬프│  ← 1 I/O (포읞터 따띌가Ʞ)
 └──┬─┘ └────┘ └────┘
    ↓
 ┌──────┐
 │ 데읎터│  ← 1 I/O (싀제 레윔드 가젞였Ʞ)
 │ 랔록 │
 └──────┘

읞덱슀륌 사용하지 않아알 할 때

읞덱슀가 항상 유익한 것은 아니닀:

  • 작은 테읎랔: 몇 개의 랔록을 순찚 슀캔하는 것읎 읞덱슀 탐색 였버헀드볎닀 빠늄
  • 높은 선택도 쿌늬(High Selectivity Queries): 쿌늬가 대부분의 행(예: 테읎랔의 15-20% 읎상)을 반환하멎, 전첎 슀캔읎 더 저렎핚
  • 빈번한 ì“°êž°: 몚든 INSERT, UPDATE, DELETE는 읞덱슀도 업데읎튞핎알 핹
  • 낮은 칎디널늬티 컬럌: B+Tree로 불늰 컬럌을 읞덱싱하는 것은 낭비임 (비튞맵 읞덱슀가 적절할 수 있음)

비용 몚덞 Ʞ쎈

디슀크 ì ‘ê·Œ 비용읎 쿌늬 성능을 지배한닀. 랔록 전송(Block Transfers) (디슀크 랔록 읜Ʞ/ì“°êž°)곌 탐색(Seeks) (디슀크 헀드 읎동) 닚위로 잡정한닀.

닀음곌 같읎 정의하자: - b = 파음의 랔록 수 - n = 레윔드 수 - f = 랔로킹 팩터(Blocking Factor) (랔록당 레윔드 수), 따띌서 b = ⌈n/f⌉

ì ‘ê·Œ 방법 평균 비용 (동등 검색)
선형 슀캔 (믞정렬) b/2 랔록 전송
선형 슀캔 (정렬) ⌈log₂(b)⌉ (읎진 검색)
죌 읞덱슀 (정렬) ⌈log₂(b_i)⌉ + 1, 여Ʞ서 b_i = 읞덱슀 랔록
B+Tree ⌈log_p(b)⌉ + 1, 여Ʞ서 p = ë¶„êž° 계수
핎시 읞덱슀 1 (읎상적) ~ 2 (였버플로우 포핚)

2. 정렬된 읞덱슀

읞덱슀 구조 Ʞ볞

읞덱슀(Index)는 검색 í‚€ 값(Search Key Values)을 포읞터(Pointers) (레윔드 식별자 또는 랔록 죌소)에 맀핑하는 데읎터 구조닀. 검색 킀는 반드시 Ʞ볞 킀음 필요는 없닀 -- ì–Žë–€ 속성읎나 속성 집합읎든 검색 킀로 사용될 수 있닀.

읞덱슀 엔튞늬 형식:
┌─────────────┬─────────────┐
│ 검색 í‚€ 값   │ 레윔드      │
│             │ 포읞터      │
└─────────────┮─────────────┘

2.1 죌 읞덱슀

죌 읞덱슀(Primary Index)는 순찚적윌로 정렬된 파음의 정렬 í‚€(Ordering Key)에 구축된닀. 파음은 읞덱싱된 속성윌로 묌늬적윌로 정렬된닀.

직원 ID에 대한 죌 읞덱슀 (파음읎 ID로 정렬됚):

읞덱슀:                         데읎터 파음:
┌─────┬──────┐                 ┌───────────────────┐
│ 100 │  ──────────────────→  │ 100 │ Alice │ ... │ 랔록 1
├─────┌───────                 │ 105 │ Bob   │ ... │
│ 200 │  ──────┐               ├────────────────────
├─────┌───────  └───────────→  │ 200 │ Carol │ ... │ 랔록 2
│ 300 │  ──────┐               │ 210 │ Dave  │ ... │
└─────┮──────┘  │              ├────────────────────
                 └───────────→ │ 300 │ Eve   │ ... │ 랔록 3
                               │ 350 │ Frank │ ... │
                               └───────────────────┘

특성: - 랔록당 하나의 읞덱슀 엔튞늬 (레윔드당읎 아님) -- 읎것은 희소 읞덱슀(Sparse Index) - 검색 킀는 파음읎 정렬된 것곌 동음한 속성 - 동등 검색곌 범위 쿌늬에 맀우 횚윚적 - 테읎랔당 하나의 죌 읞덱슀만 졎재 (파음은 한 가지 방법윌로만 정렬 가능)

2.2 큎러슀터링 읞덱슀

큎러슀터링 읞덱슀(Clustering Index)는 파음읎 묌늬적윌로 정렬된 비-í‚€ 속성에 구축된닀. 여러 레윔드가 동음한 검색 í‚€ 값을 공유할 수 있닀.

부서에 대한 큎러슀터링 읞덱슀:

읞덱슀:                         데읎터 파음 (부서로 정렬):
┌──────────┬──────┐            ┌───────────────────────────┐
│ Acctg    │  ──────────────→ │ Acctg  │ Alice │ 60000   │
├──────────┌───────            │ Acctg  │ Bob   │ 55000   │
│ Eng      │  ──────┐          ├────────────────────────────
├──────────┌───────  └──────→ │ Eng    │ Carol │ 80000   │
│ Sales    │  ──────┐          │ Eng    │ Dave  │ 75000   │
└──────────┮──────┘  │         │ Eng    │ Eve   │ 72000   │
                      │        ├────────────────────────────
                      └─────→  │ Sales  │ Frank │ 50000   │
                               │ Sales  │ Grace │ 52000   │
                               └───────────────────────────┘

특성: - 검색 킀의 고유 값당 하나의 읞덱슀 엔튞늬 - 각 포읞터는 핎당 값을 포핚하는 첫 번짞 랔록윌로 읎얎짐 - 귞룹화 및 집계 쿌늬에 횚윚적

2.3 볎조 읞덱슀

볎조 읞덱슀(Secondary Index)는 비-정렬 속성에 대한 대첎 ì ‘ê·Œ 겜로륌 제공한닀. 파음은 읎 속성윌로 정렬되얎 있지 않닀.

꞉여에 대한 볎조 읞덱슀:

읞덱슀 (꞉여로 정렬):      데읎터 파음 (ID로 정렬, ꞉여로는 정렬 안 됚):
┌───────┬──────┐               ┌───────────────────────────┐
│ 50000 │  ──────────────────→ │ 100 │ Alice │ 60000     │ 랔록 1
├───────┌───────               │ 105 │ Bob   │ 50000     │
│ 55000 │  ──────┐              ├────────────────────────────
├───────┌───────  │            │ 200 │ Carol │ 80000     │ 랔록 2
│ 60000 │  ──────┐│            │ 210 │ Dave  │ 55000     │
├───────┌───────  ││           ├────────────────────────────
│ 72000 │  ──┐    ││           │ 300 │ Eve   │ 72000     │ 랔록 3
│ ...   │    │    ││           │ 350 │ Frank │ 75000     │
└───────┮────┘    ││           └───────────────────────────┘
                  │││  (포읞터가 랔록 겜계륌 넘음)

특성: - 밀집 읞덱슀(Dense Index)여알 핹 (레윔드당 하나의 엔튞늬, 랔록당읎 아님) - 데읎터가 검색 킀로 정렬되얎 있지 않Ʞ 때묞 - 동음한 테읎랔에 여러 볎조 읞덱슀가 졎재할 수 있음 - 범위 쿌늬에 덜 횚윚적 (비순찚 데읎터로 읞한 랜덀 I/O 팹턮) - 쀑복 í‚€ 값에 대핮 추가 간접 수쀀(포읞터 버킷)읎 사용되Ʞ도 핹

밀집 vs. 희소 읞덱슀

밀집 읞덱슀(Dense Index): 데읎터 파음의 몚든 레윔드에 대핮 하나의 읞덱슀 엔튞늬.

밀집 읞덱슀:
┌─────┬───┐     ┌──────────────┐
│ 100 │ ──┌───→ │ 100 │ Alice  │
├─────┌────     │ 105 │ Bob    │ ←── 105에 대한 엔튞늬 졎재
│ 105 │ ──┌───→ │              │
├─────┌────     ├───────────────
│ 200 │ ──┌───→ │ 200 │ Carol  │
├─────┌────     │ 210 │ Dave   │
│ 210 │ ──┌───→ │              │
└─────┮───┘     └──────────────┘

희소 읞덱슀(Sparse Index): 음부 레윔드에 대한 하나의 읞덱슀 엔튞늬 (음반적윌로 랔록당 하나).

희소 읞덱슀:
┌─────┬───┐     ┌──────────────┐
│ 100 │ ──┌───→ │ 100 │ Alice  │  랔록 1
│     │   │     │ 105 │ Bob    │  (105에 대한 엔튞늬 없음)
├─────┌────     ├───────────────
│ 200 │ ──┌───→ │ 200 │ Carol  │  랔록 2
│     │   │     │ 210 │ Dave   │  (210에 대한 엔튞늬 없음)
└─────┮───┘     └──────────────┘

비교:

잡멎 밀집 읞덱슀 희소 읞덱슀
공간 더 큌 (레윔드당 엔튞늬) 더 작음 (랔록당 엔튞늬)
검색 직접 조회 가능 랔록 낮 슀캔 필요할 수 있음
요구사항 믞정렬 파음에서 작동 정렬된 데읎터 파음 필요
유지볎수 삜입/삭제 시 더 많은 업데읎튞 더 적은 업데읎튞
사용 사례 볎조 읞덱슀 죌 읞덱슀

닀닚계 읞덱슀

읞덱슀 자첎가 메몚늬에 맞지 않을 정도로 크멎, 읞덱슀에 대한 읞덱슀륌 구축할 수 있닀:

레벚 2 (왞부 읞덱슀):
┌──────┬───┐
│  1   │ ──┌───→ ┌──────┬───┐
│ 500  │ ──┌─┐   │  1   │ ──┌───→ 데읎터 랔록 1
└──────┮───┘ │   │ 100  │ ──┌───→ 데읎터 랔록 2
              │   │ 200  │ ──┌───→ 데읎터 랔록 3
              │   │ ...  │   │
              │   └──────┮───┘
              │   레벚 1 (낎부 읞덱슀, 파튾 1)
              │
              └→ ┌──────┬───┐
                 │ 500  │ ──┌───→ 데읎터 랔록 K
                 │ 600  │ ──┌───→ 데읎터 랔록 K+1
                 │ ...  │   │
                 └──────┮───┘
                 레벚 1 (낎부 읞덱슀, 파튾 2)

읎는 자연슀럜게 튞늬 구조 읞덱슀, 특히 B-Tree와 B+Tree로 읎얎진닀.


3. B-Tree

3.1 구조

찚수 m의 B-Tree (또는 degree m의 B-Tree)는 닀음을 만족한닀:

  1. 몚든 녾드는 최대 m개의 자식을 가짐
  2. 몚든 낎부 녾드(룚튞 제왞)는 최소 ⌈m/2⌉개의 자식을 가짐
  3. 룚튞는 최소 2개의 자식을 가짐 (늬프가 아닌 겜우)
  4. 몚든 늬프는 동음한 레벚에 나타낹
  5. k개의 자식을 가진 비-늬프 녾드는 k-1개의 킀륌 포핚
찚수 4의 B-Tree (각 녾드는 최대 3개의 í‚€ 볎유):

                    ┌─────────┐
                    │   30     │
                    └──┬───┬──┘
                  ┌────┘   └────┐
           ┌──────┮──┐    ┌────┮──────┐
           │ 10 │ 20 │    │ 40 │ 50   │
           └┬───┬───┬┘    └┬───┬────┬─┘
            ↓   ↓   ↓      ↓   ↓    ↓
          ┌──┐┌──┐┌──┐  ┌──┐┌──┐ ┌───┐
          │5 ││15││25│  │35││45│ │55  │
          │8 ││  ││  │  │  ││  │ │60  │
          └──┘└──┘└──┘  └──┘└──┘ └───┘

ì°žê³ : B-Tree에서는 (B+Tree와 달늬) 데읎터 포읞터가
몚든 녞드에 졎재하며, 늬프만읎 아님.

녾드 구조 (n개의 킀륌 가진 낎부 녾드):

┌────┬─────┬────┬─────┬────┬─────┬────┐
│ P₁ │ K₁  │ P₂ │ K₂  │ P₃ │ ... │ Pₙ₊₁│
│    │ D₁  │    │ D₂  │    │     │    │
└────┮─────┮────┮─────┮────┮─────┮────┘
  ↓          ↓          ↓            ↓
부튞늬    부튞늬    부튞늬      부튞늬
< K₁     [K₁,K₂)   [K₂,K₃)      ≥ Kₙ

여Ʞ서:
  Páµ¢ = 자식 녞드에 대한 포읞터 (또는 늬프의 겜우 데읎터 랔록)
  Káµ¢ = 검색 í‚€ 값
  Dáµ¢ = í‚€ Kᵢ륌 가진 데읎터 레윔드에 대한 포읞터

3.2 검색

í‚€ K에 대한 B-Tree 검색:

BTREE-SEARCH(node, K):
    i = 1
    while i ≀ node.n and K > node.key[i]:
        i = i + 1

    if i ≀ node.n and K == node.key[i]:
        return (node, i)           // 읎 녞드에서 발견

    if node is a leaf:
        return NOT_FOUND

    // 디슀크에서 자식 읜Ʞ
    DISK-READ(node.child[i])
    return BTREE-SEARCH(node.child[i], K)

복잡도: O(log_m n) 디슀크 I/O, 여Ʞ서 n은 킀의 수읎고 m은 찚수.

3.3 삜입

찚수 m의 B-Tree에 í‚€ K 삜입:

  1. ì°Ÿêž°: 검색을 사용하여 적절한 늬프 녾드 ì°Ÿêž°
  2. 삜입: 늬프 낎에 정렬된 순서로 í‚€ 삜입
  3. 녞드가 였버플로우하멎 (m개의 킀륌 가짐):
  4. 쀑간 킀에서 녞드륌 두 녞드로 분할
  5. 쀑간 킀륌 부몚로 승격
  6. 필요시 부몚 였버플로우륌 재귀적윌로 처늬

예: 찚수 3의 B-Tree에 25 삜입 (녞드당 최대 2개의 í‚€):

읎전:
        ┌────┐
        │ 20 │
        └─┬──┘
     ┌────┮─────┐
  ┌─────┐   ┌──────┐
  │10│15│   │22│30 │  ← 녞드가 ꜉ ì°ž
  └─────┘   └──────┘

닚계 1: í‚€ 25는 였륞쪜 늬프 [22, 30]에 속핚.
닚계 2: 삜입 → [22, 25, 30] — 였버플로우! (3개의 í‚€ > 최대 2)
닚계 3: 쀑간값(25)에서 분할. 25륌 부몚로 승격.

읎후:
        ┌──────┐
        │20│25 │
        └┬──┬──┘
    ┌────┘  │  └────┐
 ┌─────┐ ┌──┐  ┌──┐
 │10│15│ │22│  │30│
 └─────┘ └──┘  └──┘

3.4 삭제

찚수 m의 B-Tree에서 í‚€ K 삭제:

  1. K륌 포핚하는 녾드 ì°Ÿêž°
  2. K가 늬프에 있윌멎: 직접 제거
  3. K가 낎부 녞드에 있윌멎: 늬프에서 선행자(또는 후속자)로 교첎한 닀음 늬프에서 삭제
  4. 녞드가 얞더플로우하멎 (⌈m/2⌉ - 1개 믞만의 í‚€):
  5. 가능하멎 형제에서 재분배 (빌늬Ʞ)
  6. 귞렇지 않윌멎 형제와 병합하고 부몚에서 킀륌 낎늌
  7. 부몚 얞더플로우륌 재귀적윌로 처늬

예: 위 튞늬에서 20 삭제 (찚수 3, 비-룚튞 녞드당 최소 1개의 í‚€):

읎전:
        ┌──────┐
        │20│25 │
        └┬──┬──┘
    ┌────┘  │  └────┐
 ┌─────┐ ┌──┐  ┌──┐
 │10│15│ │22│  │30│
 └─────┘ └──┘  └──┘

닚계 1: í‚€ 20은 낎부 녞드에 있음.
닚계 2: 쀑위 선행자(15)로 교첎.
닚계 3: 늬프에서 15 삭제.

교첎 후:
        ┌──────┐
        │15│25 │
        └┬──┬──┘
    ┌────┘  │  └────┐
 ┌──┐   ┌──┐   ┌──┐
 │10│   │22│   │30│
 └──┘   └──┘   └──┘

3.5 복잡도 분석

n개의 킀륌 가진 찚수 m의 B-Tree:

연산 디슀크 I/O CPU 시간
검색 O(log_m n) O(m · log_m n)
삜입 O(log_m n) O(m · log_m n)
삭제 O(log_m n) O(m · log_m n)

높읎 한계: n ≥ 1개의 킀와 최소 찚수 t = ⌈m/2⌉에 대핮:

h ≀ log_t((n+1)/2)

음반적읞 ë¶„êž° 계수 100-200윌로, 높읎 3-4의 튞늬는 수십억개의 레윔드륌 읞덱싱할 수 있닀.


4. B+Tree

4.1 구조와 특성

B+Tree는 ꎀ계형 데읎터베읎슀에서 가장 널늬 사용되는 읞덱슀 구조닀 (PostgreSQL, MySQL InnoDB, Oracle, SQL Server 몚두 B+Tree 사용). B-Tree와 쀑요한 찚읎점:

  1. 몚든 데읎터 포읞터는 늬프 녞드에 있음 -- 낎부 녾드는 킀와 자식 포읞터만 저장
  2. 늬프 녞드가 연결됚 -- 횚윚적읞 범위 슀캔을 위한 읎쀑 연결 늬슀튞
  3. 낎부 녞드의 킀가 늬프에 쀑복됚 (낎부 킀는 가읎드 역할만)
B+Tree 구조:

낎부 녾드 (가읎드 킀만):
                    ┌────────────┐
                    │  30  │  60  │
                    └──┬───┬──┬──┘
              ┌────────┘   │  └────────┐
              ↓            ↓           ↓
        ┌─────────┐  ┌─────────┐  ┌─────────┐
        │ 10 │ 20 │  │ 40 │ 50 │  │ 70 │ 80 │
        └─┬──┬──┬─┘  └─┬──┬──┬─┘  └─┬──┬──┬─┘
          ↓  ↓  ↓      ↓  ↓  ↓      ↓  ↓  ↓

늬프 녾드 (싀제 데읎터, 연결됚):
┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐
│ 5,8 │↔│10,15│↔│20,25│↔│30,35│↔│40,45│↔│50,55│↔│60,65│↔│70,75│↔│80,85│
│     │  │     │  │     │  │     │  │     │  │     │  │     │  │     │  │     │
│ D*  │  │ D*  │  │ D*  │  │ D*  │  │ D*  │  │ D*  │  │ D*  │  │ D*  │  │ D*  │
└─────┘  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘
  ↑                                                                          ↑
Head                                              늬프 첎읞 (읎쀑 연결)     Tail

D* = 싀제 데읎터 레윔드에 대한 포읞터

녾드 형식

낎부 녾드 (찚수 m, 최대 m개의 포읞터, m-1개의 í‚€):

┌────┬─────┬────┬─────┬────┬─────┬────┐
│ P₁ │ K₁  │ P₂ │ K₂  │ P₃ │ ... │ Pₘ│
└────┮─────┮────┮─────┮────┮─────┮────┘
subtree(P₁)의 몚든 í‚€ < K₁
K₁ ≀ subtree(P₂)의 몚든 í‚€ < K₂
...
Kₘ₋₁ ≀ subtree(Pₘ)의 몚든 í‚€

늬프 녾드 (최대 m-1개의 í‚€-포읞터 쌍, 형제 포읞터 포핚):

┌──────┬──────┬──────┬──────┬─────────┐
│K₁,D₁│K₂,D₂│K₃,D₃│ ...  │ Pₙₑₓₜ  │
└──────┮──────┮──────┮──────┮─────────┘
Dáµ¢ = í‚€ Kᵢ륌 가진 레윔드에 대한 포읞터
Pₙₑₓₜ = 닀음 늬프 녞드에 대한 포읞터

4.2 B+Tree vs. B-Tree 비교

특징 B-Tree B+Tree
데읎터 포읞터 몚든 녞드에 늬프 녞드에만
í‚€ 쀑복 쀑복 없음 낎부 킀가 늬프에 쀑복됚
늬프 연결 연결 안 됚 읎쀑 연결 늬슀튞
범위 쿌늬 튞늬륌 여러 번 탐색핎알 핹 늬프 첎읞을 따띌감
ë¶„êž° 계수(Fan-out) 낮음 (데읎터 포읞터가 공간 ì°šì§€) 높음 (낎부 녞드가 더 날씬핚)
동등 검색 낎부 녞드에서 ì¡°êž° 종료 가능 항상 늬프까지 감
공간 사용 전첎적윌로 앜간 적음 앜간 많음 (í‚€ 쀑복)
싀제 사용 데읎터베읎슀에서 거의 사용 안 됚 몚든 죌요 RDBMS에서 표쀀

4.3 B+Tree에서 검색

í‚€ K에 대한 동등 검색:

BPLUS-SEARCH(root, K):
    node = root
    while node is not a leaf:
        i = smallest index such that K < node.key[i]
        if no such i exists:
            node = node.child[last]   // 가장 였륞쪜 자식
        else:
            node = node.child[i]      // 자식 i로 읎동

    // 읎제 늬프에 도달: K 슀캔
    for each (key, pointer) in node:
        if key == K:
            return pointer
    return NOT_FOUND

범위 검색 [lo, hi]의 í‚€:

BPLUS-RANGE-SEARCH(root, lo, hi):
    // 닚계 1: lo륌 포핚하는 늬프 ì°Ÿêž°
    leaf = find leaf for lo (동등 검색 로직 사용)

    // 닚계 2: 연결 늬슀튞륌 통핎 늬프 슀캔
    results = []
    while leaf is not null:
        for each (key, pointer) in leaf:
            if key > hi:
                return results
            if key >= lo:
                results.append(pointer)
        leaf = leaf.next    // 늬프 첎읞을 따띌감!
    return results

읎것읎 B+Tree가 범위 쿌늬에 뛰얎난 읎유닀 -- 시작 늬프륌 찟윌멎, 연결 늬슀튞륌 따띌가Ʞ만 하멎 된닀.

4.4 B+Tree에 삜입

데읎터 포읞터 D와 핚께 í‚€ K 삜입:

BPLUS-INSERT(root, K, D):
    leaf = find appropriate leaf node

    if leaf has room:
        insert (K, D) in sorted order in leaf
    else:
        // 늬프 였버플로우: 분할
        Create new leaf L'
        Distribute entries evenly: first ⌈m/2⌉ stay, rest go to L'

        // 복사: L'의 첫 번짞 킀가 부몚로 감
        parent-key = L'.key[1]
        INSERT-IN-PARENT(leaf, parent-key, L')

    Update leaf linked list pointers

INSERT-IN-PARENT(left, key, right):
    if parent has room:
        insert (key, right pointer) in parent
    else:
        // 낎부 녾드 였버플로우: 분할
        Create new internal node N'
        Distribute: first ⌈m/2⌉ pointers stay, rest go to N'

        // 푞시: 쀑간 킀가 부몚로 감 (복사되지 않음)
        middle-key = the key that separates the two halves
        INSERT-IN-PARENT(parent, middle-key, N')

쀑요한 구별: - 늬프 분할: 복사 (분늬 킀가 늬프와 부몚 몚두에 졎재) - 낎부 분할: 푞시 (분늬 킀가 부몚로 읎동하고 분할되는 녞드에서 제거됚)

예: 찚수 3의 B+Tree에 7 삜입 (늬프당 최대 2개의 í‚€):

읎전:
           ┌────┐
           │ 5  │
           └┬───┘
       ┌────┘  └────┐
    ┌─────┐     ┌──────┐
    │ 3,4 │ ──→ │ 5,6  │
    └─────┘     └──────┘

닚계 1: í‚€ 7은 늬프 [5, 6]에 속핚. 늬프가 ꜉ ì°ž.
닚계 2: 늬프 분할: [5]와 [6, 7]. í‚€ 6을 부몚로 복사.

읎후:
           ┌──────┐
           │ 5 │ 6│
           └┬──┬──┘
       ┌───┘  │  └───┐
    ┌─────┐ ┌───┐ ┌─────┐
    │ 3,4 │→│ 5 │→│ 6,7 │
    └─────┘ └───┘ └─────┘

4.5 B+Tree에서 삭제

í‚€ K 삭제:

BPLUS-DELETE(root, K):
    leaf = find leaf containing K
    Remove K from leaf

    if leaf has enough keys (≥ ⌈(m-1)/2⌉):
        done (첫 번짞 킀가 변겜되멎 부몚 í‚€ 업데읎튞 필요할 수 있음)
    else:
        // 얞더플로우
        if sibling has extra keys:
            redistribute (형제에서 빌늌)
            update parent key
        else:
            merge with sibling
            delete entry from parent
            recursively handle parent underflow

늬프 병합은 부몚에서 킀륌 제거한닀. 낎부 녾드 병합은 부몚에서 킀륌 낎늰닀. 룚튞가 닚음 자식만 낚윌멎, 자식읎 새 룚튞가 된닀 (튞늬 높읎 감소).

4.6 대량 로딩

빈 튞늬에서 하나씩 삜입하여 B+Tree륌 구축하는 것은 비횚윚적읎닀. 대량 로딩(Bulk Loading)은 Ʞ졎 데읎터에 읞덱슀륌 생성할 때 사용된닀:

BULK-LOAD(sorted_data, m):
    // 닚계 1: 검색 킀로 데읎터 정렬 (필요시 왞부 정렬)

    // 닚계 2: 늬프 녞드륌 순찚적윌로 채움
    for each key in sorted_data:
        add to current leaf
        if leaf full:
            start new leaf
            add separator to parent level

    // 닚계 3: 상향식윌로 낎부 레벚 구축
    repeat for each level until root is created

대량 로딩의 장점: - 랜덀 I/O 대신 순찚 I/O - 채움 비윚(Fill Factor) 제얎 가능 (예: 향후 삜입을 위핎 90% 채움) - O(n log_m n) 전첎, 하지만 상수가 훚씬 더 좋음

PostgreSQL:

-- CREATE INDEX는 테읎랔에 데읎터가 있을 때 낎부적윌로 대량 로딩 사용
CREATE INDEX idx_emp_salary ON employees(salary);

-- REINDEX는 읞덱슀륌 재구축 (대량 업데읎튞 후 유용)
REINDEX INDEX idx_emp_salary;

4.7 높읎와 성능 분석

n개의 검색 í‚€ 값을 가진 찚수 m의 B+Tree:

최대 높읎:

h ≀ ⌈log_{⌈m/2⌉}(n)⌉

싀제 예:

죌얎진: - 랔록 크Ʞ: 4 KB - í‚€ 크Ʞ: 8바읎튞, 포읞터 크Ʞ: 8바읎튞 - 낎부 녾드 ë¶„êž° 계수: m = 4096 / (8 + 8) = 256 - 늬프 엔튞늬: (m-1) = 늬프당 255

n = 100,000,000 (1억) 레윔드의 겜우:

높읎 = ⌈log₁₂₈(100,000,000)⌉ = ⌈log₁₂₈(10⁞)⌉ ≈ ⌈8/2.1⌉ ≈ 4

따띌서 1억 개 쀑 ì–Žë–€ 레윔드든 4번의 디슀크 읜Ʞ로 찟을 수 있닀. 싀제로 룚튞와 상위 레벚은 메몚늬에 캐시되얎, 읎륌 1-2번의 디슀크 읜Ʞ로 쀄음 수 있닀.


5. 핎시 êž°ë°˜ 읞덱싱

5.1 정적 핎싱

핎시 읞덱슀는 핎시 핚수 h(K)륌 사용하여 검색 í‚€ 값을 버킷 죌소에 직접 맀핑한닀.

핎시 핚수 h가 í‚€ K륌 버킷 번혞에 맀핑:

Key: "Alice" → h("Alice") = 2
Key: "Bob"   → h("Bob")   = 0
Key: "Carol" → h("Carol") = 2  (충돌!)

버킷 ë°°ì—Ž:
┌─────────┐
│ 버킷 0  │ → [Bob, ...]
├──────────
│ 버킷 1  │ → [empty]
├──────────
│ 버킷 2  │ → [Alice, ...] → [Carol, ...]  (였버플로우 첎읞)
├──────────
│ 버킷 3  │ → [Dave, ...]
└─────────┘

특성: - 읎상적읞 겜우: O(1) 조회 -- 한 번의 디슀크 읜Ʞ - 였버플로우 처늬: 첎읎닝 (였버플로우 버킷의 연결 늬슀튞) - 앜점: 고정된 버킷 수. 파음읎 슝가하멎 성능 저하

핎시 핚수 요구사항: 1. 균음 분포: 킀가 버킷 전첎에 균등하게 분산되얎알 핹 2. 결정적: 동음한 킀는 항상 동음한 버킷에 맀핑 3. 빠륞 계산: O(1) 시간

5.2 확장 가능 핎싱

확장 가능 핎싱(Extendible Hashing)은 전첎 파음을 재구성하지 않고 필요에 따띌 크Ʞ가 2배가 되는 디렉토늬륌 사용하여 데읎터 슝가에 적응한닀.

죌요 개념: - 전역 깊읎(Global Depth) (d): 디렉토늬가 사용하는 핎시 값의 비튞 수 - 로컬 깊읎(Local Depth) (dáµ¢): 버킷 i가 사용하는 비튞 수 - 디렉토늬 크Ʞ = 2^d 엔튞늬

전역 깊읎 d = 2읞 예:

핎시 값 (2진수):        디렉토늬 (d=2):
h(K₁) = 01001...            ┌────┬───────────┐
h(K₂) = 10110...            │ 00 │ ──→ 버킷 A (로컬 깊읎 2)
h(K₃) = 00101...            │ 01 │ ──→ 버킷 B (로컬 깊읎 2)
h(K₄) = 11010...            │ 10 │ ──→ 버킷 C (로컬 깊읎 1)
h(K₅) = 10011...            │ 11 │ ──→ 버킷 C (로컬 깊읎 1)
                             └────┮───────────┘

ì°žê³ : 엔튞늬 10곌 11 몚두 버킷 C륌 가늬킎
C의 로컬 깊읎 (1) < 전역 깊읎 (2)읎Ʞ 때묞.
버킷 C에는 첫 번짞 비튞만 쀑요핚.

버킷 였버플로우 시 버킷 분할:

버킷 B (로컬 깊읎 2)가 였버플로우하멎:

쌀읎슀 1: 로컬 깊읎 < 전역 깊읎
  → 버킷 분할, 로컬 깊읎 슝가
  → 엔튞늬 재분배
  → 디렉토늬 포읞터 업데읎튞

쌀읎슀 2: 로컬 깊읎 == 전역 깊읎
  → 디렉토늬륌 2배로 (전역 깊읎 1 슝가)
  → 버킷 분할, 로컬 깊읎 슝가
  → 엔튞늬 재분배
  → 디렉토늬 포읞터 업데읎튞

장점: - 였버플로우 첎읞 없음 (분할읎 슝가 처늬) - 조회당 최대 2번의 디슀크 ì ‘ê·Œ (디렉토늬 1번, 버킷 1번) - 우아하게 슝가

닚점: - 데읎터가 치우치멎 디렉토늬가 컀질 수 있음 - 디렉토늬 2ë°° 확장은 비싞지만 드묌게 발생

5.3 선형 핎싱

선형 핎싱(Linear Hashing)은 디렉토늬륌 완전히 플한닀. 버킷은 분할 포읞터로 제얎되는 띌욎드 로빈 방식윌로 한 번에 하나씩 분할된닀.

핵심 아읎디얎: - 닀음 분할할 버킷을 추적하는 분할 포읞터 s 유지 - 두 개의 핎시 핚수 사용: h₀(K) = K mod N곌 h₁(K) = K mod 2N - 버킷읎 였버플로우하멎, 분할 포읞터의 버킷을 분할 (였버플로우된 버킷읎 아님)

N=4 버킷, 분할 포읞터 s=1읞 상태:

버킷:
┌──────────┐
│ 버킷 0   │ → [h₁(K)=0읞 레윔드]    (읎믞 분할됚, h₁ 사용)
├───────────
│ 버킷 1   │ → [h₀(K)=1읞 레윔드]    ← 분할 포읞터 s
├───────────
│ 버킷 2   │ → [h₀(K)=2읞 레윔드]    (아직 분할 안 됚, h₀ 사용)
├───────────
│ 버킷 3   │ → [h₀(K)=3읞 레윔드]    (아직 분할 안 됚, h₀ 사용)
├───────────
│ 버킷 4   │ → [h₁(K)=4읞 레윔드]    (버킷 0 분할로 생성됚)
└──────────┘

조회 알고늬슘:
  b = h₀(K)          // 예: K mod 4
  if b < s:           // 버킷읎 읎믞 분할됚
      b = h₁(K)      // 대신 K mod 8 사용
  search bucket b

ì–žì œ 분할할까: - ì–Žë–€ 버킷읎 였버플로우하멎 분할읎 튞늬거됚 - 분할 포읞터의 버킷읎 분할됚 (였버플로우된 버킷읎 아님!) - 였버플로우된 버킷은 임시로 였버플로우 첎읞 사용 - 분할 포읞터 전진: s = s + 1 - s = N읎멎, 띌욎드 완료: N = 2N, s = 0, 핎시 핚수 전진

장점: - 디렉토늬 불필요 - 부드러욎 슝가 (한 번에 하나의 버킷) - 볎장된 O(1) 평균 ì ‘ê·Œ

닚점: - 임시 였버플로우 첎읞 - 분할읎 싀제 였버플로우된 버킷을 슉시 완화하지 못할 수 있음

핎시 vs. B+Tree 비교

Ʞ쀀 핎시 읞덱슀 B+Tree 읞덱슀
동등 쿌늬 O(1) -- 우수 O(log n) -- 좋음
범위 쿌늬 지원 안 핹 우수 (늬프 첎읞)
정렬된 순회 지원 안 핹 자연슀러움 (늬프 슀캔)
동적 슝가 확장 가능/선형 분할곌 병합
공간 사용 공간 낭비 가능 (빈 버킷) 좋은 활용 (~67%)
구현 더 닚순 더 복잡
RDBMS에서 음반적 PostgreSQL (핎시), Redis 몚든 죌요 RDBMS

6. 비튞맵 읞덱슀

6.1 개념

비튞맵 읞덱슀(Bitmap Index)는 읞덱싱된 속성의 각 고유 값에 대핮 비튞 벡터륌 생성한닀. 각 비튞는 테읎랔의 행에 핎당한닀.

테읎랔: employees (8개 행)
┌─────┬───────┬────────┬────────┐
│ RID │ Name  │ Dept   │ Gender │
├─────┌───────┌────────┌─────────
│  0  │ Alice │ Eng    │ F      │
│  1  │ Bob   │ Sales  │ M      │
│  2  │ Carol │ Eng    │ F      │
│  3  │ Dave  │ HR     │ M      │
│  4  │ Eve   │ Eng    │ F      │
│  5  │ Frank │ Sales  │ M      │
│  6  │ Grace │ HR     │ F      │
│  7  │ Hank  │ Eng    │ M      │
└─────┮───────┮────────┮────────┘

Dept에 대한 비튞맵 읞덱슀:
  Eng:   [1, 0, 1, 0, 1, 0, 0, 1]  (행 0,2,4,7)
  Sales: [0, 1, 0, 0, 0, 1, 0, 0]  (행 1,5)
  HR:    [0, 0, 0, 1, 0, 0, 1, 0]  (행 3,6)

Gender에 대한 비튞맵 읞덱슀:
  F:     [1, 0, 1, 0, 1, 0, 1, 0]  (행 0,2,4,6)
  M:     [0, 1, 0, 1, 0, 1, 0, 1]  (행 1,3,5,7)

6.2 비튞맵 연산

비튞 연산을 사용하여 비튞맵을 결합하여 쿌늬에 답할 수 있닀:

쿌늬: 몚든 여성 엔지니얎 ì°Ÿêž°

  Dept=Eng:  [1, 0, 1, 0, 1, 0, 0, 1]
  AND
  Gender=F:  [1, 0, 1, 0, 1, 0, 1, 0]
  ─────────────────────────────────────
  결곌:      [1, 0, 1, 0, 1, 0, 0, 0]  → 행 0, 2, 4
                                         (Alice, Carol, Eve)
쿌늬: Sales 또는 HR의 몚든 직원 ì°Ÿêž°

  Dept=Sales: [0, 1, 0, 0, 0, 1, 0, 0]
  OR
  Dept=HR:    [0, 0, 0, 1, 0, 0, 1, 0]
  ─────────────────────────────────────
  결곌:       [0, 1, 0, 1, 0, 1, 1, 0]  → 행 1, 3, 5, 6
쿌늬: 몚든 비-엔지니얎 ì°Ÿêž°

  NOT Dept=Eng: NOT [1, 0, 1, 0, 1, 0, 0, 1]
  ─────────────────────────────────────────────
  결곌:             [0, 1, 0, 1, 0, 1, 1, 0]  → 행 1, 3, 5, 6

6.3 비튞맵 읞덱슀륌 사용핎알 할 때

읎상적읞 겜우: - 낮은 칎디널늬티 속성 (Gender: 2개 값, Status: 3-5개 값) - 데읎터 웚얎하우징 - 대부분 읜Ʞ 전용 ì ‘ê·Œ - 복잡한 닀쀑 속성 쿌늬 (AND/OR 조합) - 칎욎튞 쿌늬 (WHERE가 있는 COUNT) -- 섀정된 비튞만 섞Ʞ - 슀타 슀킀마(Star Schema) 팩튞 테읎랔의 많은 찚원 왞래 í‚€

읎상적읎지 않은 겜우: - 높은 칎디널늬티 속성 (예: 고유 ID -- 값당 하나의 비튞맵!) - OLTP - 빈번한 업데읎튞 (몚든 삜입마닀 비튞맵 업데읎튞는 비쌈) - 높은 동시성 환겜 (비튞맵 잠ꞈ읎 많은 행에 영향)

6.4 비튞맵 압축

대형 테읎랔의 겜우 비튞맵읎 맀우 컀질 수 있닀. 압축 Ʞ법읎 도움읎 된닀:

런 Ꞟ읎 읞윔딩(Run-Length Encoding, RLE):

원볞:  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
RLE:   (0, 11), (1, 1), (0, 4)    → "11개의 0, 1개의 1, 4개의 0"

워드 정렬 하읎람늬드(Word-Aligned Hybrid, WAH): Oracle 및 닀륞 시슀템에서 사용. 동음한 워드(32 또는 64비튞)의 시퀀슀륌 압축하멎서 압축된 형태로 횚윚적읞 비튞 연산을 허용한닀.

로얎링 비튞맵(Roaring Bitmaps): 비튞맵을 청크로 분할하고 각 청크에 대핮 최적의 표현(ë°°ì—Ž, 비튞맵, 또는 런)을 사용하는 현대적읞 압축 Ʞ법. Apache Lucene, Apache Spark 및 닀륞 시슀템에서 널늬 사용된닀.

6.5 공간 분석

n개 행곌 c개 고유 값을 가진 속성을 가진 테읎랔:

비압축 비튞맵 크Ʞ = n × c 비튞 = nc/8 바읎튞

B+Tree와 비교:
B+Tree 읞덱슀 크Ʞ ≈ n × (key_size + pointer_size) 바읎튞

예: 100만 행, 5개 값을 가진 속성: - 비튞맵: 1,000,000 x 5 / 8 = 625,000 바읎튞 ≈ 610 KB - B+Tree: 1,000,000 x (8 + 8) = 16,000,000 바읎튞 ≈ 15.3 MB

낮은 칎디널늬티 속성의 겜우 비튞맵은 25ë°° 더 작닀.


7. 닀찚원 읞덱싱

7.1 묞제

표쀀 B+Tree와 핎시 읞덱슀는 1찚원 검색 킀에 잘 작동한닀. 하지만 많은 쿌늬는 여러 찚원을 포핚한닀:

-- 공간 쿌늬
SELECT * FROM restaurants
WHERE latitude BETWEEN 37.7 AND 37.8
  AND longitude BETWEEN -122.5 AND -122.4;

-- 닀쀑 속성 범위 쿌늬
SELECT * FROM products
WHERE price BETWEEN 10 AND 50
  AND weight BETWEEN 0.5 AND 2.0;

(latitude, longitude)에 대한 B+Tree는 latitude에서 횚윚적윌로 필터링할 수 있지만 longitude에 대핎서는 음치하는 몚든 엔튞늬륌 슀캔핎알 한닀. 진정한 닀찚원 읞덱슀는 두 찚원을 동시에 처늬한닀.

7.2 R-Tree

R-Tree (Rectangle Tree)는 가장 널늬 사용되는 공간 읞덱슀닀. 최소 겜계 사각형(Minimum Bounding Rectangles, MBR)을 사용하여 데읎터륌 구성한닀.

R-Tree 구조:

룚튞: [MBR₁, MBR₂]
       ┌──────────────────────────────────────┐
       │   ┌───────────┐    ┌──────────────┐  │
       │   │   MBR₁    │    │    MBR₂      │  │
       │   │ ┌──┐ ┌──┐ │    │ ┌───┐  ┌──┐  │  │
       │   │ │P1│ │P2│ │    │ │P3 │  │P4│  │  │
       │   │ └──┘ └──┘ │    │ └───┘  └──┘  │  │
       │   │      ┌──┐ │    │     ┌──┐     │  │
       │   │      │P5│ │    │     │P6│     │  │
       │   │      └──┘ │    │     └──┘     │  │
       │   └───────────┘    └──────────────┘  │
       └──────────────────────────────────────┘

각 낎부 녾드: [MBR₁, ptr₁, MBR₂, ptr₂, ...]
각 늬프 녾드: [MBR₁, oid₁, MBR₂, oid₂, ...]

특성: - 균형 튞늬 (몚든 늬프가 동음한 레벚에) - 각 녾드는 ⌈m/2⌉와 m 사읎의 엔튞늬륌 포핚 - 죌얎진 레벚의 MBR읎 겹칠 수 있음 - 검색은 여러 겜로륌 따띌가알 할 수 있음 (B+Tree와 달늬)

연산: - 검색: 룚튞에서 시작, MBR읎 쿌늬 사각형곌 겹치는 몚든 자식윌로 낎렀감 - 삜입: MBR읎 가장 적게 확대되얎알 하는 부튞늬륌 선택; 필요시 분할 - 사용처: PostgreSQL (GiST 읞덱슀), Oracle Spatial, PostGIS

7.3 kd-Tree

kd-tree (k-찚원 튞늬)는 각 레벚에서 분할 찚원을 교대로 하여 공간을 분할한닀.

2D kd-tree 예 (x, ê·ž 닀음 y, ê·ž 닀음 x로 분할, ...):

데읎터 포읞튞: (2,3), (5,4), (9,6), (4,7), (8,1), (7,2)

                    (7,2)          x=7에서 분할
                   /     \
              (5,4)       (9,6)    y=4, y=6에서 분할
             /    \          \
          (2,3)  (4,7)      (8,1)  x=2, x=4, x=8에서 분할

공간 분할:
┌───────────────────────┐
│           │            │
│  (2,3)    │   (9,6)   │
│    ·      │     ·     │
│───────(5,4)──────     │
│    ·  │   │           │
│ (4,7) │   │  (8,1)    │
│       │   │    ·      │
└───────┮───┮───────────┘
        x=7    (분할선)

특성: - 읎진 튞늬 (각 녞드가 공간을 반윌로 분할) - 낮은 찚원 데읎터에 횚윚적 (d ≀ 20) - 검색: O(n^(1-1/d) + k) 여Ʞ서 k는 결곌 수 - 동적 데읎터에 대핮 균형읎 맞지 않음 (k-d-B 튞늬와 같은 변형 사용)

공간 읞덱슀 비교:

특징 R-Tree kd-Tree
찚원 ì–Žë–€ d에도 작동 낮은 d에 최적
동적 업데읎튞 좋음 (읎륌 위핎 섀계됚) 나쁚 (불균형 가능)
디슀크 êž°ë°˜ 예 (읎륌 위핎 섀계됚) 원래 읞-메몚늬
겹칚 겹칚 허용 겹칚 없음
사용 사례 GIS, 공간 데읎터베읎슀 k-NN 검색, 읞-메몚늬

8. 읞덱슀 선택곌 섀계 지칚

8.1 읞덱슀 선택 묞제

올바륞 읞덱슀륌 선택하는 것은 성능에 쀑요하닀. 읞덱슀 선택 묞제는 닀음을 묻는닀: 워크로드(쿌늬 집합)가 죌얎졌을 때, ì–Žë–€ 읞덱슀륌 생성핎알 하는가?

고렀할 요읞: 1. 쿌늬 팹턮: WHERE, JOIN, ORDER BY, GROUP BY에 ì–Žë–€ 컬럌읎 나타나는가? 2. 업데읎튞 빈도: INSERT, UPDATE, DELETE가 얌마나 자죌 싀행되는가? 3. 데읎터 분포: 각 컬럌의 칎디널늬티와 선택도 4. 저장 예산: 각 읞덱슀는 디슀크 공간곌 유지볎수 였버헀드가 듬 5. 상ꎀꎀ계: 자죌 핚께 쿌늬되는 컬럌

8.2 컀버링 읞덱슀

컀버링 읞덱슀(Covering Index)는 쿌늬에 필요한 몚든 컬럌을 포핚하므로, 데읎터베읎슀가 테읎랔에 접귌하지 않고 읞덱슀만윌로 쿌늬에 답할 수 있닀 (읞덱슀 전용 슀캔).

-- 쿌늬:
SELECT name, salary FROM employees WHERE department = 'Eng';

-- 비-컀버링 읞덱슀 (테읎랔 ì ‘ê·Œ 필요):
CREATE INDEX idx_dept ON employees(department);
-- 읞덱슀륌 통핎 음치하는 행을 찟은 닀음, 테읎랔에서 name, salary륌 가젞옎

-- 컀버링 읞덱슀 (테읎랔 ì ‘ê·Œ 불필요):
CREATE INDEX idx_dept_covering ON employees(department, name, salary);
-- 필요한 몚든 컬럌읎 읞덱슀에 있음

PostgreSQL INCLUDE 구묞:

-- INCLUDE 컬럌은 늬프 레벚에 저장되지만 검색 킀에는 없음
CREATE INDEX idx_dept_incl ON employees(department) INCLUDE (name, salary);
-- department는 검색 í‚€; name곌 salary는 페읎로드만

읎점: - 테읎랔 ì ‘ê·Œ 제거 (막대한 I/O 절감) - 자죌 싀행되는 쿌늬에 특히 횚곌적

튞레읎드였프: - 더 큰 읞덱슀 크Ʞ - 업데읎튞 시 더 많은 컬럌 유지볎수

8.3 복합 (닀쀑 컬럌) 읞덱슀

복합 읞덱슀(Composite Index)는 여러 컬럌에 구축된닀. 컬럌 순서가 맀우 쀑요하닀.

CREATE INDEX idx_dept_salary ON employees(department, salary);

읎 읞덱슀는 닀음에 유용하닀:

-- 전첎 읞덱슀 사용 (두 컬럌 몚두):
SELECT * FROM employees WHERE department = 'Eng' AND salary > 80000;

-- 읞덱슀 접두사 사용 (department만):
SELECT * FROM employees WHERE department = 'Eng';

-- 읎 읞덱슀륌 횚윚적윌로 사용하지 않음:
SELECT * FROM employees WHERE salary > 80000;  -- salary는 접두사가 아님

최좌 접두사 규칙: (A, B, C)에 대한 복합 읞덱슀는 닀음 쿌늬에 사용될 수 있닀: - A - A, B - A, B, C

하지만 닀음에는 횚윚적읎지 않닀: - B 당독 - C 당독 - B, C

8.4 부분 읞덱슀

부분 읞덱슀(Partial Index)는 조걎자로 정의된 행의 부분집합만 읞덱싱한닀.

-- 활성 직원만 읞덱싱 (90%가 비활성읎멎 읞덱슀가 훚씬 작음)
CREATE INDEX idx_active_emp ON employees(name)
WHERE status = 'active';

-- 최귌 죌묞만 읞덱싱
CREATE INDEX idx_recent_orders ON orders(customer_id)
WHERE order_date > '2025-01-01';

-- 비-NULL 값만 읞덱싱
CREATE INDEX idx_email ON users(email)
WHERE email IS NOT NULL;

읎점: - 더 작은 읞덱슀 크Ʞ (더 적은 엔튞늬) - 더 빠륞 유지볎수 (더 적은 업데읎튞) - 더 나은 캐시 활용

8.5 표현식 읞덱슀

음부 데읎터베읎슀는 계산된 표현식 읞덱싱을 지원한닀:

-- PostgreSQL: 표현식 읞덱슀
CREATE INDEX idx_lower_email ON users(lower(email));

-- 대소묞자 구분 없는 검색에 유용:
SELECT * FROM users WHERE lower(email) = 'alice@example.com';

-- 타임슀탬프에서 연도 추출한 읞덱슀:
CREATE INDEX idx_order_year ON orders(EXTRACT(YEAR FROM order_date));

8.6 싀용적 지칚

규칙 1: WHERE, JOIN, ORDER BY의 컬럌 읞덱싱

-- 읎 쿌늬는 customer_id, order_date, status의 읞덱슀로 읎익
SELECT o.* FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.order_date > '2025-01-01'
ORDER BY o.status;

규칙 2: 선택도 ê³ ë € - 높은 선택도 (음치하는 행읎 적음) = 좋은 읞덱슀 후볎 - 낮은 선택도 (대부분의 행읎 음치) = 나쁜 읞덱슀 후볎 - 겜험 법칙: 읞덱슀는 행의 15-20% 믞만을 선택할 때 유용

규칙 3: 곌도한 읞덱싱 플하Ʞ

각 읞덱슀 비용:
- 디슀크 공간 (읞덱슀 구조 크Ʞ)
- 삜입 였버헀드: ~읞덱슀당 1번의 B+Tree 삜입
- 업데읎튞 였버헀드: 읞덱슀당 구 삭제 + 신 삜입
- 진공/유지볎수 였버헀드

음반적읞 지칚: 테읎랔당 3-5개 읞덱슀, 거의 10개륌 넘지 않음

규칙 4: EXPLAIN을 사용하여 읞덱슀 사용 확읞

EXPLAIN ANALYZE SELECT * FROM employees WHERE department = 'Eng';

-- 찟을 것:
-- "Index Scan" 또는 "Index Only Scan" → 읞덱슀가 사용됚
-- "Seq Scan" → 읞덱슀가 사용되지 않음 (또는 졎재하지 않음)
-- "Bitmap Index Scan" → 닀쀑 조걎 쿌늬에 비튞맵 읞덱슀 사용됚

규칙 5: 읞덱슀 몚니터링 및 유지볎수

-- PostgreSQL: 읞덱슀 사용 통계 확읞
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;
-- idx_scan = 0읞 읞덱슀는 사용되지 않윌며 삭제 가능

-- 읞덱슀 크Ʞ 확읞
SELECT pg_size_pretty(pg_relation_size('idx_emp_salary'));

-- 당펾화된 읞덱슀 재구축
REINDEX INDEX idx_emp_salary;

9. 죌요 데읎터베읎슀의 읞덱슀 구조

PostgreSQL

-- B+Tree (Ʞ볞값)
CREATE INDEX idx_btree ON table(column);

-- 핎시 (동등 검색만, v10부터 WAL 로깅됚)
CREATE INDEX idx_hash ON table USING hash(column);

-- GiST (음반화된 검색 튞늬(Generalized Search Tree) -- R-Tree, 전묞 검색 등)
CREATE INDEX idx_gist ON table USING gist(geom_column);

-- GIN (음반화된 역 읞덱슀(Generalized Inverted Index) -- ë°°ì—Ž, JSONB, 전묞 검색)
CREATE INDEX idx_gin ON table USING gin(jsonb_column);

-- BRIN (랔록 범위 읞덱슀(Block Range Index) -- 대형 정렬 테읎랔)
CREATE INDEX idx_brin ON table USING brin(timestamp_column);

-- SP-GiST (공간 분할 GiST(Space-Partitioned GiST) -- kd-튞늬, Ʞ수 튞늬)
CREATE INDEX idx_spgist ON table USING spgist(point_column);

MySQL InnoDB

- Ʞ볞 í‚€ = 큎러슀터드 B+Tree 읞덱슀 (데읎터가 늬프 녞드에 저장됚)
- 볎조 읞덱슀는 Ʞ볞 í‚€ 값을 저장 (행 포읞터가 아님)
- 읎는 볎조 읞덱슀 조회가 두 번의 B+Tree 탐색을 필요로 핚을 의믞:
  1. 볎조 읞덱슀 → Ʞ볞 í‚€ 값
  2. Ʞ볞 읞덱슀 → 싀제 행 데읎터

요앜 표

읞덱슀 타입 최적 용도 한계
B+Tree 범용, 범위, ORDER BY 닀찚원에는 별로
핎시 정확한 동등 조회 범위 지원 없음
비튞맵 낮은 칎디널늬티, 분석 OLTP, 높은 칎디널늬티에 나쁚
GiST/R-Tree 공간, Ʞ하 데읎터 겹칚읎 검색을 느늬게 할 수 있음
GIN 전묞 검색, ë°°ì—Ž, JSONB 큰 읞덱슀, 느며 업데읎튞
BRIN 맀우 큰, 자연 정렬 테읎랔 낮은 정밀도

10. 연습묞제

개념적 질묞

연습묞제 1: 볎조 읞덱슀는 밀집핎알 하는 반멎 죌 읞덱슀는 희소할 수 있는 읎유륌 섀명하띌 (레윔드당 하나의 엔튞늬 vs. 랔록당 하나의 엔튞늬).

연습묞제 2: 테읎랔에 500,000개의 레윔드가 각각 4 KB 랔록에 저장되얎 있닀. 각 레윔드는 200바읎튞닀. Ʞ볞 킀에 대한 B+Tree 읞덱슀는 8바읎튞 킀와 8바읎튞 포읞터륌 사용한닀. 각 읞덱슀 녾드는 하나의 랔록(4 KB)읎닀.

(a) 하나의 데읎터 랔록에 몇 개의 레윔드가 맞는가? (b) 테읎랔은 몇 개의 데읎터 랔록을 찚지하는가? (c) B+Tree의 최대 ë¶„êž° 계수(찚수)는? (d) B+Tree의 최대 높읎는? (e) 읞덱슀륌 사용한 동등 검색 vs. 전첎 테읎랔 슀캔에 몇 번의 디슀크 I/O가 필요한가?

연습묞제 3: B-Tree와 B+Tree륌 비교하띌. 거의 몚든 데읎터베읎슀 시슀템읎 B-Tree 대신 B+Tree륌 사용하는 읎유는?

연습묞제 4: 1천만 행을 가진 테읎랔에서 8개의 고유 값을 가진 color 컬럌에 비튞맵 읞덱슀가 생성되었닀.

(a) 비튞맵 읞덱슀의 쎝 비압축 크Ʞ는? (b) 행의 0.1%만 color = 'purple'읎멎, 런 Ꞟ읎 읞윔딩을 사용하여 purple 비튞맵을 얌마나 압축할 수 있는가? (c) 읎륌 동음한 컬럌의 B+Tree 읞덱슀와 비교하띌.

싀용적 질묞

연습묞제 5: 닀음 쿌늬 워크로드에 대한 읞덱싱 전략을 섀계하띌:

-- Q1 (튞래픜의 90%): 정확한 조회
SELECT * FROM users WHERE email = ?;

-- Q2 (튞래픜의 5%): 정렬을 포핚한 범위 슀캔
SELECT name, created_at FROM users
WHERE created_at > ? ORDER BY created_at DESC LIMIT 20;

-- Q3 (튞래픜의 3%): 닀쀑 컬럌 필터
SELECT * FROM users
WHERE country = ? AND age BETWEEN ? AND ?;

-- Q4 (튞래픜의 2%): 텍슀튞 검색
SELECT * FROM users WHERE name ILIKE '%smith%';

각 쿌늬에 대핮 명시하띌: (a) ì–Žë–€ 읞덱슀(또는 읞덱슀듀)륌 생성할 것읞가? (b) ì–Žë–€ 타입의 읞덱슀(B+Tree, 핎시, GIN 등)? (c) 복합, 컀버링, 또는 부분 읞덱슀륌 사용할 것읞가?

연습묞제 6: 전역 깊읎 2, 버킷 용량 2읞 닀음 확장 가능 핎시 디렉토늬가 죌얎졌닀:

디렉토늬:          버킷:
00 → 버킷 A [h=00110, h=00010]  (로컬 깊읎 2)
01 → 버킷 B [h=01100]            (로컬 깊읎 2)
10 → 버킷 C [h=10001, h=10110]  (로컬 깊읎 2)
11 → 버킷 D [h=11000]            (로컬 깊읎 2)

핎시 값 h = 00001을 가진 레윔드륌 삜입한 후 디렉토늬와 버킷의 상태륌 볎여띌.

연습묞제 7: 4개의 쎈Ʞ 버킷(N=4), 분할 포읞터 s=0, 버킷 용량 2읞 선형 핎싱 방식을 고렀하띌. 핎시 핚수는 h₀(K) = K mod 4와 h₁(K) = K mod 8읎닀. 닀음윌로 시작:

버킷 0: [8, 16]   (꜉ ì°ž)
버킷 1: [5]
버킷 2: [10]
버킷 3: [7, 15]   (꜉ ì°ž)

í‚€ 12, 9, 3을 (하나씩) 삜입한 후 상태륌 볎여띌.

분석 질묞

연습묞제 8: 찚수 m곌 n개의 킀륌 가진 B+Tree의 높읎가 최대 ⌈log_{⌈m/2⌉}(n)⌉임을 슝명하띌.

연습묞제 9: 데읎터베읎슀 ꎀ늬자가 5천만 행을 가진 테읎랔에서 SELECT COUNT(*) FROM orders WHERE status = 'pending' 쿌늬가 2쎈 걞늰닀는 것을 발견했닀. status 컬럌은 5개의 고유 값을 가진닀. 읞덱싱 전략을 권장하고 개선을 추정하띌.

연습묞제 10: sensor_data 테읎랔에 10억 개의 행, 컬럌 (sensor_id, timestamp, value, location_x, location_y)가 있고, 닀음 쿌늬 팚턎읎 있닀:

  • 시간 범위 쿌늬: WHERE timestamp BETWEEN ? AND ?
  • 섌서별 쿌늬: WHERE sensor_id = ? AND timestamp BETWEEN ? AND ?
  • 공간 쿌늬: WHERE location_x BETWEEN ? AND ? AND location_y BETWEEN ? AND ?
  • 집계: SELECT sensor_id, AVG(value) FROM ... GROUP BY sensor_id

포ꎄ적읞 읞덱싱 전략을 섀계하띌. B+Tree, BRIN, GiST, 복합 읞덱슀륌 고렀하띌. 선택을 정당화하띌.


읎전: 질의 처늬와 최적화 | 닀음: 튞랜잭션 읎론

to navigate between lessons