Pingu
영차영차! Backend

데이터베이스 인덱스 완전 정리: B-Tree 인덱스 vs 역인덱스

2026년 1월 12일
2개 태그
Inverted Index
Index

데이터베이스 인덱스 완전 정리: B-Tree 인덱스 vs 역인덱스

개요

데이터베이스에서 빠른 검색을 구현하려면 인덱스가 필수입니다. 하지만 인덱스에도 여러 종류가 있고, 각각의 특성과 사용 목적이 다릅니다. 이 글에서는 전통적인 인덱스(B-Tree 인덱스)와 역인덱스(Inverted Index)의 차이를 이해하고, 언제 어떤 것을 사용해야 하는지 알아봅니다.

이 글을 읽고 나면:

  • 인덱스와 역인덱스의 기본 원리를 이해할 수 있습니다
  • 각 인덱스의 장단점과 사용 사례를 파악할 수 있습니다
  • MongoDB Atlas Search에서 역인덱스가 어떻게 활용되는지 알 수 있습니다

문제 상황: 왜 인덱스가 필요한가?

인덱스 없이 검색할 때

수백만 개의 문서가 있는 컬렉션에서 특정 이름을 가진 문서를 찾는다고 가정해봅시다. 인덱스가 없다면 데이터베이스는 모든 문서를 하나씩 확인해야 합니다.

문서 1: { name: "김철수", ... }
문서 2: { name: "이영희", ... }
문서 3: { name: "박민수", ... }
...
문서 1,000,000: { name: "안유진", ... }

“안유진”을 찾기 위해 1번부터 1,000,000번까지 순차적으로 검색해야 합니다. 이는 O(n) 시간 복잡도로, 데이터가 많아질수록 검색 시간이 선형적으로 증가합니다.

인덱스가 있을 때

인덱스가 있으면 데이터베이스는 인덱스 구조를 먼저 확인하여 해당 문서의 위치를 빠르게 찾을 수 있습니다. B-Tree 인덱스의 경우 O(log n) 시간 복잡도로 검색할 수 있어 훨씬 빠릅니다.

전통적인 인덱스(B-Tree 인덱스)

기본 원리

전통적인 인덱스는 책의 색인과 비슷합니다. 책에서 특정 단어를 찾을 때 처음부터 끝까지 읽지 않고, 색인 페이지를 먼저 확인하여 해당 단어가 나오는 페이지 번호를 찾는 것과 같습니다.

구조 예시

다음과 같은 문서들이 있다고 가정합니다:

문서 ID: 1, name: "안유진"
문서 ID: 2, name: "김민지"
문서 ID: 3, name: "이해인"
문서 ID: 4, name: "안유진"
문서 ID: 5, name: "박지은"

B-Tree 인덱스는 다음과 같은 구조를 가집니다:

        [김민지]
       /        \
  [이해인]    [안유진]
   /    \      /    \
[박지은] [null] [null] [null]

각 노드는 값과 해당 문서 ID를 저장합니다. “안유진”을 검색하면 트리를 탐색하여 문서 ID 1과 4를 빠르게 찾을 수 있습니다.

장점

  1. 정확한 값 검색에 효율적: name = "안유진"과 같은 정확한 일치 검색이 빠릅니다
  2. 범위 검색에 유리: age >= 20 AND age <= 30과 같은 범위 쿼리에 최적화되어 있습니다
  3. 정렬된 결과: 인덱스가 정렬된 상태를 유지하므로 ORDER BY가 빠릅니다

단점

  1. 부분 문자열 검색에 비효율적: name LIKE "%유진%"과 같은 검색은 인덱스를 활용하기 어렵습니다
  2. 전체 텍스트 검색 부적합: 여러 단어를 동시에 검색하거나 관련도 순으로 정렬하기 어렵습니다
  3. 복합 검색의 한계: 여러 필드를 조합한 복잡한 검색 쿼리에 제약이 있습니다

역인덱스(Inverted Index)

기본 원리

역인덱스는 검색 엔진의 핵심 구조입니다. 각 단어(토큰)를 키로 하고, 해당 단어가 포함된 문서들의 목록을 값으로 저장합니다.

구조 예시

다음 문서들이 있다고 가정합니다:

문서 1: { name: "안유진", introduction: "아이브의 리더" }
문서 2: { name: "김민지", introduction: "뉴진스의 리더" }
문서 3: { name: "이해인", introduction: "아이브의 멤버" }

역인덱스는 다음과 같은 구조를 가집니다:

"안유진" → [문서 1]
"김민지" → [문서 2]
"이해인" → [문서 3]
"아이브" → [문서 1, 문서 3]
"리더" → [문서 1, 문서 2]
"뉴진스" → [문서 2]
"멤버" → [문서 3]

“아이브”를 검색하면 문서 1과 문서 3을 즉시 찾을 수 있습니다. “아이브”와 “리더”를 동시에 검색하면 두 리스트의 교집합을 구하여 문서 1을 찾을 수 있습니다.

분석 과정

역인덱스를 만들기 위해서는 텍스트를 토큰으로 분리하는 과정이 필요합니다. 이를 텍스트 분석(Text Analysis)이라고 합니다.

예를 들어, “아이브의 리더”라는 텍스트는 다음과 같이 분석됩니다:

원본 텍스트: "아이브의 리더"
↓ (토큰화)
["아이브", "의", "리더"]
↓ (불용어 제거 - 선택적)
["아이브", "리더"]
↓ (어간 추출 - 선택적)
["아이브", "리더"]

한국어의 경우 형태소 분석이 필요하므로 lucene.nori 같은 분석기를 사용합니다.

장점

  1. 전체 텍스트 검색에 최적화: 여러 단어를 동시에 검색하고 관련도 순으로 정렬할 수 있습니다
  2. 부분 문자열 검색 지원: 단어 단위로 검색하므로 유연한 검색이 가능합니다
  3. 복합 검색 효율적: 여러 필드를 조합한 복잡한 검색 쿼리를 빠르게 처리합니다
  4. 관련도 점수 계산: 각 문서의 검색어 관련도를 계산하여 정확한 순서로 결과를 제공합니다

단점

  1. 정확한 값 검색에 비효율적: 단일 값 검색은 B-Tree 인덱스보다 느릴 수 있습니다
  2. 범위 검색 부적합: 숫자나 날짜 범위 검색에는 적합하지 않습니다
  3. 저장 공간: 각 단어마다 문서 리스트를 저장하므로 더 많은 저장 공간이 필요합니다
  4. 인덱스 구축 시간: 텍스트 분석 과정이 필요하므로 인덱스 구축에 시간이 걸립니다

비교: 언제 무엇을 사용할까?

B-Tree 인덱스가 적합한 경우

  1. 정확한 값 검색
// 사용자 ID로 조회
db.users.find({ userId: 'user123' });
  1. 범위 검색
// 나이 범위로 검색
db.users.find({ age: { $gte: 20, $lte: 30 } });
  1. 정렬된 결과
// 가입일 순으로 정렬
db.users.find().sort({ createdAt: -1 });

역인덱스가 적합한 경우

  1. 전체 텍스트 검색
// 제목이나 내용에서 검색
db.articles.find({ $text: { $search: 'MongoDB Atlas Search' } });
  1. 복합 검색
// 여러 필드에서 동시에 검색
{
  compound: {
    should: [
      { text: { query: '안유진', path: 'name' } },
      { text: { query: '안유진', path: 'introduction' } },
    ];
  }
}
  1. 관련도 순 정렬
// 검색어와의 관련도에 따라 자동 정렬
// 점수가 높은 문서가 먼저 반환됨

MongoDB Atlas Search에서의 역인덱스

MongoDB Atlas Search는 역인덱스를 기반으로 작동합니다. Atlas Search 인덱스를 생성하면 자동으로 역인덱스가 구축됩니다.

인덱스 정의 예시

{
  "mappings": {
    "dynamic": false,
    "fields": {
      "name": {
        "type": "string",
        "analyzer": "lucene.nori",
        "searchAnalyzer": "lucene.nori"
      },
      "introduction": {
        "type": "string",
        "analyzer": "lucene.nori",
        "searchAnalyzer": "lucene.nori"
      }
    }
  }
}

이 인덱스는 다음과 같은 역인덱스를 생성합니다:

"안유진" → [문서 ID: 1, 4]
"아이브" → [문서 ID: 1, 3, 5]
"리더" → [문서 ID: 1, 2]
...

검색 쿼리 예시

db.idols.aggregate([
  {
    $search: {
      index: 'korean_text_index',
      compound: {
        should: [
          {
            text: {
              query: '안유진',
              path: 'name',
              score: { boost: { value: 10 } },
            },
          },
          {
            text: {
              query: '안유진',
              path: 'introduction',
              score: { boost: { value: 3 } },
            },
          },
        ],
      },
    },
  },
]);

이 쿼리는 다음과 같이 처리됩니다:

  1. “안유진”이라는 토큰을 역인덱스에서 찾습니다
  2. name 필드에서 찾은 문서는 10점, introduction 필드에서 찾은 문서는 3점을 받습니다
  3. 점수가 높은 순서로 결과를 정렬하여 반환합니다

실제 사용 사례

사례 1: 전자상거래 상품 검색

전자상거래 사이트에서 상품을 검색할 때는 역인덱스가 필수입니다.

사용자 검색어: "무선 이어폰 블루투스"

B-Tree 인덱스로는 이 검색을 효율적으로 처리하기 어렵습니다. 하지만 역인덱스를 사용하면:

  1. “무선”, “이어폰”, “블루투스” 각각의 문서 리스트를 찾습니다
  2. 세 리스트의 교집합을 구합니다
  3. 관련도 점수를 계산하여 정렬합니다

사례 2: 사용자 프로필 검색

사용자 ID로 정확히 조회할 때는 B-Tree 인덱스가 적합합니다.

사용자 ID: "user_12345"

B-Tree 인덱스는 O(log n) 시간에 정확한 문서를 찾을 수 있습니다.

사례 3: 블로그 포스트 검색

블로그 포스트의 제목과 내용을 검색할 때는 역인덱스가 필요합니다.

검색어: "NestJS MongoDB 연동"

여러 필드에서 동시에 검색하고, 관련도 순으로 정렬하여 사용자에게 가장 관련성 높은 포스트를 먼저 보여줄 수 있습니다.

성능 비교

검색 속도

검색 유형B-Tree 인덱스역인덱스
정확한 값 검색매우 빠름 (O(log n))보통 (O(1) ~ O(n))
범위 검색빠름 (O(log n))느림 (전체 스캔)
전체 텍스트 검색느림 (전체 스캔)매우 빠름 (O(1))
복합 검색느림빠름

저장 공간

  • B-Tree 인덱스: 원본 데이터의 약 10-20% 추가 공간 필요
  • 역인덱스: 원본 데이터의 약 30-50% 추가 공간 필요 (텍스트 분석 결과 저장)

요약

B-Tree 인덱스

  • 용도: 정확한 값 검색, 범위 검색, 정렬
  • 장점: 빠른 정확한 검색, 효율적인 범위 쿼리
  • 단점: 전체 텍스트 검색에 부적합

역인덱스

  • 용도: 전체 텍스트 검색, 복합 검색, 관련도 순 정렬
  • 장점: 유연한 텍스트 검색, 관련도 점수 계산
  • 단점: 저장 공간이 많이 필요, 인덱스 구축 시간 소요

선택 가이드

  • 정확한 값이나 범위로 검색 → B-Tree 인덱스
  • 텍스트 내용을 검색 → 역인덱스
  • 둘 다 필요 → 두 인덱스를 모두 생성

결론

인덱스와 역인덱스는 각각 다른 목적을 위해 설계되었습니다. B-Tree 인덱스는 정확한 값 검색과 범위 쿼리에 최적화되어 있고, 역인덱스는 전체 텍스트 검색과 복합 검색에 최적화되어 있습니다.

MongoDB에서는 일반 쿼리에는 B-Tree 인덱스를, Atlas Search를 통한 텍스트 검색에는 역인덱스를 사용합니다. 프로젝트의 요구사항에 맞는 인덱스를 선택하여 검색 성능을 최적화하세요.

FAQ

Q1: B-Tree 인덱스와 역인덱스를 동시에 사용할 수 있나요?

네, 가능합니다. MongoDB에서는 일반 쿼리용 B-Tree 인덱스와 Atlas Search용 역인덱스를 별도로 생성할 수 있습니다. 각각의 목적에 맞게 사용하면 됩니다.

Q2: 역인덱스는 언제 구축되나요?

MongoDB Atlas Search의 경우, 인덱스를 생성하면 백그라운드에서 자동으로 역인덱스가 구축됩니다. 데이터가 많을 경우 시간이 걸릴 수 있으므로 background: true 옵션을 사용하는 것이 좋습니다.

Q3: 역인덱스의 크기는 어떻게 확인하나요?

MongoDB Atlas 콘솔의 Search 인덱스 페이지에서 인덱스 크기를 확인할 수 있습니다. 일반적으로 원본 데이터의 30-50% 정도의 크기를 가집니다.

Q4: 한국어 검색을 위해 특별한 설정이 필요한가요?

한국어는 형태소 분석이 필요하므로 lucene.nori 분석기를 사용해야 합니다. 이 분석기는 한국어 텍스트를 형태소 단위로 분리하여 역인덱스를 구축합니다.

Q5: 인덱스가 성능에 미치는 영향은?

인덱스는 검색 속도를 크게 향상시키지만, 데이터를 추가하거나 수정할 때 인덱스도 함께 업데이트해야 하므로 쓰기 성능에 약간의 영향을 줄 수 있습니다. 대부분의 경우 읽기 성능 향상이 쓰기 성능 저하보다 훨씬 큽니다.

댓글

?