Elasticsearch 아키텍처와 핵심 개념
왜 Elasticsearch인가?
MySQL이나 PostgreSQL은 LIKE '%검색어%' 쿼리로도 텍스트를 찾을 수 있다. 하지만 이 방법은 인덱스를 타지 못해 데이터가 많아지면 풀 테이블 스캔이 발생한다. "오늘 저녁 피자 먹고 싶다"라는 문장이 있을 때 "피자"로 검색하려면 형태소를 분석하고 토큰화해야 하는데, RDBMS는 이런 언어 처리를 기본으로 지원하지 않는다.
Elasticsearch는 Apache Lucene 위에 구축된 분산 검색·분석 엔진이다. 2010년 Shay Banon이 처음 오픈소스로 공개했고, 현재는 Elastic이 개발·운영하며 오픈소스(ELv2/SSPL)와 Elastic Cloud 서비스를 함께 제공한다. 전문(full-text) 검색뿐 아니라 로그 분석(ELK 스택), APM, 보안 이벤트 집계에 폭넓게 쓰인다.
역 인덱스: 전문 검색의 기반
역 인덱스(inverted index)는 "어떤 단어가 어떤 문서에 있는가"를 미리 만들어 두는 자료구조다. RDBMS의 B-Tree 인덱스가 "행 → 값"을 빠르게 찾는 구조라면, 역 인덱스는 "값(토큰) → 문서 목록"을 저장한다.
검색어 "kafka"가 들어오면 역 인덱스에서 [doc1, doc2]를 O(1)에 조회한다. 분석기(analyzer)는 입력 텍스트를 토큰으로 쪼개고(tokenizer), 소문자 변환·불용어 제거·형태소 분석 같은 필터를 적용한다. 한국어는 nori 플러그인이 형태소를 처리한다.
클러스터 계층 구조
Elasticsearch의 데이터 모델은 네 계층으로 이루어진다.
| 계층 | 설명 |
|---|---|
| 클러스터 | 동일한 cluster.name을 공유하는 노드의 집합. 단일 논리 시스템으로 동작 |
| 인덱스 | 관련 문서의 모음. RDBMS의 테이블에 해당. 실제로는 하나 이상의 샤드로 분산 |
| 샤드(Shard) | Lucene 인스턴스 하나. 인덱스를 수평 분할한 단위. 기본값 1개(7.x 이후) |
| 레플리카(Replica) | 프라이머리 샤드의 복사본. 고가용성 + 읽기 성능 향상 |
프라이머리 샤드 수는 인덱스 생성 시점에 고정된다. 데이터가 많아지면 샤드를 늘릴 수 없으므로, 처음부터 예상 데이터 크기를 고려해 설정해야 한다. 레플리카는 언제든 변경 가능하다.
프라이머리 샤드와 그 레플리카는 같은 노드에 배치되지 않는다. 노드 하나가 죽어도 레플리카가 프라이머리로 승격되어 서비스가 이어진다.
노드 역할
프로덕션 환경에서는 노드에 역할을 분리해 성능과 안정성을 높인다.
- Master-eligible node: 클러스터 메타데이터(인덱스 매핑, 샤드 배치)를 관리. 홀수 대수로 운영(뇌분리 방지).
- Data node: 실제 샤드를 저장하고 색인·검색 연산을 수행. 디스크와 I/O 집약적.
- Coordinating node: 클라이언트 요청을 받아 관련 샤드에 분산하고 결과를 취합. 전용 코디네이터는 힙 메모리 부담을 줄여준다.
- Ingest node: 색인 전 파이프라인(grok, geoip, rename 등) 처리. Logstash 없이 간단한 전처리를 클러스터 내에서 처리할 때 사용.
소규모 클러스터에서는 한 노드가 여러 역할을 겸한다. Elastic 공식 권고는 노드 3대 이상, 마스터 전용 3대, 데이터 전용 N대 분리다.
문서 색인 흐름: Buffer → Segment → Disk
문서를 색인할 때 즉시 디스크에 쓰지 않는다. 세 단계로 나뉜다.
- Translog 기록: 색인 직후 내구성을 위해 트랜잭션 로그에 기록된다. 노드 재시작 시 미커밋 연산을 복구한다.
- Refresh (기본 1초): 인메모리 버퍼가 새 Lucene 세그먼트로 변환되고 검색 가능해진다. 아직 디스크엔 없다.
refresh_interval: -1로 비활성화해 대량 색인 성능을 높일 수 있다. - Flush (기본 30분 or translog 512 MB): Lucene이 세그먼트를 디스크에 커밋(fsync)하고 translog를 초기화한다.
세그먼트는 불변(immutable)이다. 문서 업데이트는 실제로 새 세그먼트에 새 버전을 쓰고 기존 세그먼트의 문서를 삭제 표시(tombstone)한다. 작은 세그먼트들은 백그라운드 병합(merge)으로 통합된다.
분산 쿼리 실행: Query + Fetch 2단계
검색 요청은 코디네이팅 노드를 통해 두 단계로 실행된다.
- Query phase: 코디네이터가 관련 샤드(프라이머리 또는 레플리카) 전체에 쿼리를 브로드캐스트한다. 각 샤드는 자신의 역 인덱스에서 매칭 문서의
_id와 점수를 반환한다. - Fetch phase: 코디네이터가 상위 N개
_id를 선별해 해당 샤드에 실제 문서 본문을 요청하고 취합해 반환한다.
이 구조 덕분에 쿼리는 여러 샤드에서 병렬 처리된다. 반면 from + size 페이지네이션 깊이가 깊어질수록 각 샤드가 더 많은 상위 결과를 반환해야 하므로 메모리 부담이 커진다(deep pagination 문제).
한 줄 정리
Elasticsearch는 역 인덱스 기반의 Lucene을 분산 계층으로 감싼 검색·분석 엔진이며, 샤드를 여러 노드에 분산해 대규모 데이터의 색인·검색을 병렬 처리한다.
References
- https://www.elastic.co/docs/deploy-manage/distributed-architecture/clusters-nodes-shards
- https://opster.com/guides/elasticsearch/glossary/elasticsearch-flush-translog-and-refresh/
- https://learncsdesigns.medium.com/demystifying-elasticsearch-understanding-nodes-clusters-shards-and-indices-part-2-c1835e1e061e
- https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/
- https://shayne007.github.io/2025/06/16/Elasticsearch-Underlying-Principles-Deep-Dive/
- https://medium.com/@bohyunlee.dev/understanding-opensearch-data-indexing-and-translog-f67d65b2b920