[Daily morning study] Full-text Search와 역인덱스(Inverted Index) — Elasticsearch 동작 원리

#daily morning study

Image


Full-text Search란

Full-text Search(전문 검색)는 텍스트 데이터 전체를 분석해서 특정 단어나 구문이 포함된 문서를 찾는 검색 방식이다. 일반 RDBMS의 LIKE '%keyword%' 방식과 근본적으로 다른 접근을 사용한다.

LIKE 검색의 한계

  • LIKE '%검색어%'는 인덱스를 활용하지 못하고 전체 테이블 스캔(Full Table Scan)을 수행한다.
  • 동의어, 형태소 분석이 없어 “실행”으로 검색하면 “실행하다”, “실행됨”은 매칭되지 않는다.
  • 관련성(relevance) 점수를 계산하지 않아 결과 정렬이 어렵다.

Full-text Search 엔진은 이 문제를 역인덱스(Inverted Index) 자료구조로 해결한다.


역인덱스(Inverted Index) 구조

역인덱스는 “어떤 문서에 어떤 단어가 있는가”가 아니라 “어떤 단어가 어느 문서에 있는가” 방향으로 만든 인덱스다.

예시 문서:

문서 ID내용
doc1“Redis는 인메모리 데이터베이스다”
doc2“데이터베이스 인덱스는 성능을 높인다”
doc3“Redis 클러스터는 데이터를 분산한다”

역인덱스:

토큰(Term)문서 목록 (Posting List)
redis[doc1, doc3]
인메모리[doc1]
데이터베이스[doc1, doc2]
인덱스[doc2]
클러스터[doc3]
분산[doc3]

“Redis”를 검색하면 Posting List에서 [doc1, doc3]을 O(1)에 가까운 속도로 찾아낸다. 테이블 전체를 스캔할 필요가 없다.


텍스트 분석 파이프라인 (Text Analysis)

문서를 역인덱스에 넣기 전에 분석(Analysis) 단계를 거친다. 이 과정이 검색 품질을 결정한다.

원문 텍스트
   ↓
Character Filter  → HTML 태그 제거, 특수문자 변환 등
   ↓
Tokenizer         → 텍스트를 토큰(단어) 단위로 분리
   ↓
Token Filter      → 소문자 변환, 불용어 제거, 형태소 분석 등
   ↓
역인덱스에 저장

예시: “Running faster than EVER!”

  1. Tokenizer → ["Running", "faster", "than", "EVER"]
  2. Lowercase Filter → ["running", "faster", "than", "ever"]
  3. Stop Word Filter (불용어 제거) → ["running", "faster", "ever"]
  4. Stemming (어간 추출) → ["run", "fast", "ever"]

이렇게 하면 “run”, “runs”, “running” 모두 동일한 토큰 run으로 매칭된다.


Elasticsearch 아키텍처

Elasticsearch는 Apache Lucene 기반의 분산 검색 엔진으로, 역인덱스를 분산 환경에서 관리한다.

핵심 개념

개념설명RDBMS 대응
Index문서의 논리적 컨테이너Database / Table
Document검색 단위, JSON 형태Row
Field문서의 속성Column
Shard인덱스를 나눈 물리적 단위Partition
Replica샤드의 복제본Replication

클러스터 구조

Elasticsearch Cluster
├── Node 1 (Primary)
│   ├── Shard 0 (Primary)
│   └── Shard 1 (Replica)
├── Node 2
│   ├── Shard 1 (Primary)
│   └── Shard 2 (Replica)
└── Node 3
    ├── Shard 2 (Primary)
    └── Shard 0 (Replica)
  • Primary Shard: 원본 데이터 저장 및 쓰기 처리
  • Replica Shard: 읽기 부하 분산 및 장애 복구용 복제본
  • Primary와 Replica는 항상 다른 노드에 위치해 고가용성 보장

샤드 수 설정

인덱스 생성 시 Primary Shard 수를 지정하며, 인덱스 생성 이후에는 변경 불가하다. (Replica는 변경 가능)

PUT /my_index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

검색 과정 (Query Execution)

Scatter-Gather 패턴

  1. Scatter: 클라이언트 요청을 받은 노드(Coordinating Node)가 관련 샤드 전체에 검색 요청 브로드캐스트
  2. Gather: 각 샤드의 결과를 수집, 관련성 점수(Relevance Score) 기준으로 병합 및 정렬
  3. 최종 결과 반환

관련성 점수 (TF-IDF / BM25)

Elasticsearch는 기본적으로 BM25 알고리즘으로 문서 관련성을 계산한다.

  • TF (Term Frequency): 문서 내 검색어 등장 빈도 — 많이 나올수록 높은 점수
  • IDF (Inverse Document Frequency): 전체 문서 대비 검색어 희소성 — 희귀한 단어일수록 높은 가중치
  • BM25: TF-IDF를 개선한 알고리즘으로 문서 길이 보정(Field Length Normalization) 추가
BM25 Score = IDF × (TF × (k1 + 1)) / (TF + k1 × (1 - b + b × dl/avgdl))
  • k1: TF 포화 상수 (기본 1.2)
  • b: 문서 길이 보정 계수 (기본 0.75)
  • dl/avgdl: 현재 문서 길이 / 평균 문서 길이

Elasticsearch vs RDBMS 쿼리 비교

RDBMS의 LIKE 검색:

SELECT * FROM articles
WHERE content LIKE '%데이터베이스%'
ORDER BY created_at DESC;

Elasticsearch의 Full-text Search:

GET /articles/_search
{
  "query": {
    "match": {
      "content": "데이터베이스"
    }
  },
  "sort": [
    { "_score": { "order": "desc" } }
  ]
}

Elasticsearch는 토큰 분석 → 역인덱스 조회 → BM25 점수 계산을 거쳐 관련성이 높은 문서를 빠르게 반환한다.


근사 검색과 자동완성

역인덱스 기반 검색의 강점은 정확한 단어 매칭 외에 다양한 검색 방식을 지원한다는 점이다.

검색 유형설명예시
match기본 전문 검색, 분석기 적용“데이터 베이스” → 2개 토큰 OR 검색
match_phrase구문 검색, 순서 유지“인메모리 데이터베이스” 그대로
fuzzy오타 허용 검색 (Levenshtein 거리)“Elasticsaerch” → Elasticsearch
prefix접두사 검색“redi” → redis, redisson
wildcard와일드카드 패턴“r*s” → redis, rabbitmq
range범위 검색날짜, 숫자 범위

Near Real-Time (NRT) 특성

Elasticsearch는 완전한 실시간이 아닌 Near Real-Time 검색을 지원한다.

문서 색인 흐름:

문서 쓰기
   ↓
In-memory Buffer (즉시)
   ↓  (Refresh, 기본 1초)
Segment (검색 가능 상태)
   ↓  (Flush, 기본 30분 or translog 512MB)
Disk (영구 저장)
  • Refresh: 메모리 버퍼를 새 Segment로 만들어 검색 가능 상태로 전환 (기본 1초 간격)
  • Flush: Segment를 디스크에 영구 기록, Translog 초기화
  • 따라서 문서를 색인한 직후 최대 1초 후부터 검색 결과에 노출된다.

언제 Elasticsearch를 쓰는가

사용 적합사용 부적합
대용량 텍스트 전문 검색강한 트랜잭션/ACID 보장 필요
로그 분석 (ELK Stack)복잡한 관계형 조인
자동완성, 추천 검색어단순 키-값 조회 (Redis가 더 적합)
다국어 형태소 분석 검색소규모 단순 검색
복잡한 집계 쿼리 (Aggregation) 

일반적으로 주 저장소는 RDBMS(MySQL, PostgreSQL), 검색/분석은 Elasticsearch로 분리하는 아키텍처를 자주 사용한다. RDBMS에 쓰기가 발생하면 CDC나 Logstash를 통해 Elasticsearch에 동기화한다.