[ELK Deep Dive] Elasticsearch — Elasticsearch 소개
PART 1. Elasticsearch 소개
1. Elasticsearch란
2. 핵심 개념 훑어보기 — Index, Document, Mapping
PART 2. 검색 원리
3. 역인덱스 — Elasticsearch의 검색 원리
PART 3. 주요 기능과 강점
4. Elasticsearch의 주요 기능과 강점
PART 4. 마무리
5. 다음 편 미리보기 — Index 안은 어떻게 생겼나
Elasticsearch는 대량의 데이터를 저장하고, 빠르게 검색하고, 실시간으로 분석하기 위한 분산 검색 엔진입니다. 이 글에서는 Elasticsearch가 무엇이고, 어떤 원리로 검색을 수행하는지 살펴봅니다.
Elasticsearch는 Apache Lucene 위에 만들어진 분산 검색 및 분석 엔진입니다.
이 문장을 풀어보면 두 가지 핵심이 있습니다.
그런데 왜 Lucene을 직접 쓰지 않고 Elasticsearch를 쓰는 걸까요? Lucene은 라이브러리입니다. 클러스터 관리, 복제, REST API, 장애 복구 같은 운영에 필요한 기능이 없습니다. Elasticsearch는 이런 부분을 해결해서, Lucene의 검색 능력을 분산 환경에서 바로 쓸 수 있게 만든 것입니다.
┌─────────────────────────────────────────────────────────┐
│ Elasticsearch │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 클러스터 관리 / REST API / 분산 검색 조율 / 복제 │ │
│ └───────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼───────────────────────────┐ │
│ │ Apache Lucene │ │
│ │ 텍스트 분석 / 역인덱스 생성 / Segment 검색 │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Elasticsearch는 보통 단독으로 쓰이기보다 ELK 스택의 일부로 사용됩니다. ELK는 Elasticsearch, Logstash, Kibana의 앞 글자를 딴 이름입니다.
┌──────────────┐ ┌──────────────────┐ ┌────────────────┐
│ Logstash │ │ Elasticsearch │ │ Kibana │
│ │ │ │ │ │
│ 수집 + 가공 │────▶│ 저장 + 검색 │────▶│ 시각화 + 분석 │
│ │ │ │ │ │
└──────────────┘ └──────────────────┘ └────────────────┘
여러 소스에서 JSON 문서로 대시보드,
로그/데이터를 저장하고 차트, 알림으로
파싱하고 변환 검색/집계 데이터를 시각화
각 컴포넌트의 역할을 정리하면 다음과 같습니다.
| 컴포넌트 | 역할 | 비유 |
|---|---|---|
| Logstash | 여러 소스에서 데이터를 수집하고, 파싱/변환하여 Elasticsearch로 전송 | 데이터 배관공 |
| Elasticsearch | 데이터를 저장하고 검색/집계 | 검색 엔진 + 저장소 |
| Kibana | Elasticsearch의 데이터를 시각화하고 대시보드로 분석 | 모니터링 화면 |
이 시리즈에서는 Elasticsearch의 내부 동작에 집중합니다. Logstash와 Kibana는 Elasticsearch를 더 편리하게 쓰기 위한 도구라고 이해하면 충분합니다.
Elasticsearch는 "검색이 필요한 곳"이라면 어디든 쓰입니다. 대표적인 세 가지를 살펴봅니다.
전문 검색 (Full-text Search)
쇼핑몰에서 "무선 블루투스 이어폰"을 검색하면, 상품명에 정확히 "무선 블루투스 이어폰"이라고 적혀있지 않아도 관련 상품이 나옵니다. "블루투스 무선 이어폰", "BT 이어폰 무선"처럼 단어 순서가 다르거나 표현이 달라도 매칭됩니다. 자동완성도 마찬가지입니다. "주문"만 타이핑해도 "주문 생성", "주문 취소", "주문 내역 조회"가 즉시 제안됩니다.
RDB의 LIKE 검색으로는 이런 동작이 어렵습니다. 왜 어려운지는 3장에서 역인덱스를 설명할 때 다루겠습니다.
로그 분석 (ELK 스택)
서비스에서 하루에 수천만 건의 로그가 쌓인다고 가정합니다. 장애가 발생했을 때 "최근 1시간 동안 OrderService에서 발생한 ERROR 로그 중 timeout이 포함된 것"을 찾아야 합니다. RDB에 로그를 넣었다면, 수천만 건을 대상으로 여러 조건을 걸어 검색해야 합니다. Elasticsearch는 이런 조건부 전문 검색을 밀리초 단위로 처리합니다.
실시간 분석 (대시보드, 모니터링)
"지난 5분간 결제 실패율이 5%를 넘었는가?" 같은 실시간 집계도 Elasticsearch의 영역입니다. 데이터가 저장되면 거의 즉시 검색과 집계에 반영되기 때문에, Kibana 대시보드에서 실시간에 가까운 모니터링이 가능합니다.
핵심 정리: Elasticsearch는 Lucene의 검색 능력 위에 분산/운영 레이어를 얹은 검색 엔진이며, 전문 검색, 로그 분석, 실시간 모니터링이 대표적인 활용 분야입니다.
Elasticsearch를 처음 접하면 용어가 낯설게 느껴집니다. 하지만 RDB 경험이 있다면 대응시켜 이해할 수 있습니다. 이 섹션에서는 핵심 용어 네 가지를 정리하고, RDB와의 대응 관계를 살펴봅니다.
Document
Elasticsearch에서 데이터의 기본 단위입니다. JSON 형태로 저장됩니다.
{
"orderId": "ORD-12345",
"status": "COMPLETED",
"amount": 35000,
"createdAt": "2025-02-21T14:30:00",
"message": "주문이 정상적으로 생성되었습니다"
}
RDB에서 하나의 Row(행)에 해당합니다.
Field
Document 안의 각 항목입니다. 위 예시에서 orderId, status, amount, createdAt, message가 각각 하나의 Field입니다. RDB의 Column에 해당합니다.
Index
Document의 논리적 모음입니다. 같은 성격의 Document를 하나의 Index에 저장합니다. 예를 들어 주문 로그는 order-logs라는 Index에, 결제 로그는 payment-logs라는 Index에 저장합니다. RDB의 Table에 대응됩니다.
Mapping
Index에 저장되는 Document의 구조를 정의합니다. 각 Field의 타입(text, keyword, long, date 등)을 지정합니다. RDB의 Schema에 해당합니다.
{
"mappings": {
"properties": {
"orderId": { "type": "keyword" },
"status": { "type": "keyword" },
"amount": { "type": "long" },
"createdAt": { "type": "date" },
"message": { "type": "text" }
}
}
}
| RDB | Elasticsearch | 설명 |
|---|---|---|
| Database | Cluster | 전체 시스템 |
| Table | Index | 데이터의 논리적 모음 |
| Row | Document | 데이터의 기본 단위 |
| Column | Field | 개별 속성 |
| Schema | Mapping | 구조 정의 |
RDB 대응은 첫 이해에는 도움이 되지만, 실제 사용 방식에는 차이가 있습니다.
가장 큰 차이는 Index의 사용 패턴입니다. RDB에서는 orders라는 하나의 테이블에 모든 주문 데이터를 넣습니다. 하지만 Elasticsearch에서는 order-logs-2025-02-21, order-logs-2025-02-22처럼 날짜별로 Index를 분리하는 것이 일반적입니다.
┌──────────────────────────────────────────────────────┐
│ RDB │
│ │
│ orders 테이블 (하나) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 2025-02-19 데이터 │ │
│ │ 2025-02-20 데이터 │ │
│ │ 2025-02-21 데이터 │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Elasticsearch │
│ │
│ order-logs-2025-02-19 (Index 1) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 2025-02-19 데이터 │ │
│ └─────────────────────────────────────────────────┘ │
│ order-logs-2025-02-20 (Index 2) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 2025-02-20 데이터 │ │
│ └─────────────────────────────────────────────────┘ │
│ order-logs-2025-02-21 (Index 3) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 2025-02-21 데이터 │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
왜 이렇게 할까요? Elasticsearch에서는 order-logs-2025-02-*처럼 와일드카드로 여러 Index를 한 번에 검색할 수 있습니다. 그리고 오래된 데이터를 삭제할 때, Index 단위로 통째로 삭제하는 것이 행 단위 DELETE보다 압도적으로 빠릅니다. 이런 관점에서 보면 Elasticsearch의 Index는 RDB의 Table보다는 파티션(Partition)에 더 가깝습니다.
이 부분은 2편에서 Index의 내부 구조(Shard, Segment)를 다루면서 더 자세히 설명하겠습니다.
핵심 정리: Elasticsearch는 JSON 문서를 Index에 저장하고, Mapping으로 구조를 정의합니다. RDB 용어로 대응하면 이해가 쉽지만, Index의 사용 패턴은 Table보다 Partition에 가깝다는 점을 기억해야 합니다.
Elasticsearch가 빠른 검색을 할 수 있는 가장 근본적인 이유는 역인덱스(Inverted Index)에 있습니다. 이 섹션에서는 역인덱스가 무엇이고, 왜 일반적인 인덱스보다 전문 검색에 유리한지를 살펴봅니다.
RDB에서 익숙한 B-Tree 인덱스를 생각해봅니다. orders 테이블에 orderId 컬럼으로 인덱스를 만들면, orderId가 "ORD-12345"인 행을 빠르게 찾을 수 있습니다.
이것은 문서(Row) → 값(Column) 방향의 인덱스입니다. 특정 컬럼의 특정 값을 기준으로 행을 찾습니다.
┌──────────────────────────────────────────────┐
│ Forward Index (B-Tree) │
│ │
│ 문서 → 포함된 값 │
│ │
│ Doc 1 → "주문 생성이 완료되었습니다" │
│ Doc 2 → "재고 부족으로 주문이 실패했습니다" │
│ Doc 3 → "주문 생성 후 결제가 진행됩니다" │
└──────────────────────────────────────────────┘
그런데 이 상태에서 "주문 생성"이라는 텍스트가 포함된 문서를 찾으려면 어떻게 해야 할까요? 모든 문서를 하나씩 열어서 "주문 생성"이 들어있는지 확인해야 합니다. 이것이 RDB에서 LIKE '%주문 생성%'이 느린 이유입니다. 풀 스캔(Full Scan) 을 해야 하기 때문입니다.
문서가 100건이면 100건 전부, 1억 건이면 1억 건 전부를 확인해야 합니다.
역인덱스는 방향을 뒤집습니다. 단어(토큰) → 문서 방향입니다.
┌──────────────────────────────────────────────┐
│ Inverted Index │
│ │
│ 단어(토큰) → 해당 단어를 포함한 문서 목록 │
│ │
│ "주문" → [Doc 1, Doc 2, Doc 3] │
│ "생성" → [Doc 1, Doc 3] │
│ "완료" → [Doc 1] │
│ "재고" → [Doc 2] │
│ "부족" → [Doc 2] │
│ "실패" → [Doc 2] │
│ "결제" → [Doc 3] │
│ "진행" → [Doc 3] │
└──────────────────────────────────────────────┘
이제 "주문 생성"을 검색하면, 모든 문서를 확인할 필요가 없습니다. 역인덱스에서 "주문"과 "생성"을 각각 찾아서, 두 결과의 교집합을 구하면 됩니다.
실무에 가까운 로그 3건으로 과정을 따라가 봅니다.
Step 1: 문서 저장
다음 세 건의 로그가 Elasticsearch에 저장됩니다.
| 문서 | message 필드 |
|---|---|
| Doc 1 | "주문 생성이 완료되었습니다" |
| Doc 2 | "재고 부족으로 주문이 실패했습니다" |
| Doc 3 | "주문 생성 후 결제가 진행됩니다" |
Step 2: 텍스트 분석 (토큰화)
Elasticsearch는 문서를 저장할 때, 텍스트 필드의 값을 Analyzer로 분석합니다. Analyzer는 텍스트를 의미 있는 단위(토큰)로 쪼개는 역할을 합니다. 여기서는 한국어 형태소 분석이 적용된다고 가정합니다.
| 문서 | 원본 | 토큰 |
|---|---|---|
| Doc 1 | "주문 생성이 완료되었습니다" | ["주문", "생성", "완료"] |
| Doc 2 | "재고 부족으로 주문이 실패했습니다" | ["재고", "부족", "주문", "실패"] |
| Doc 3 | "주문 생성 후 결제가 진행됩니다" | ["주문", "생성", "결제", "진행"] |
"생성이", "주문이", "부족으로" 같은 조사가 제거되고, "완료되었습니다" 같은 어미가 정리됩니다. 이것이 Analyzer의 역할이며, 3편에서 상세히 다룹니다.
Step 3: 역인덱스 구축
토큰화 결과를 바탕으로 역인덱스 테이블이 만들어집니다.
| 토큰 | 문서 목록 (Posting List) |
|---|---|
| 주문 | Doc 1, Doc 2, Doc 3 |
| 생성 | Doc 1, Doc 3 |
| 완료 | Doc 1 |
| 재고 | Doc 2 |
| 부족 | Doc 2 |
| 실패 | Doc 2 |
| 결제 | Doc 3 |
| 진행 | Doc 3 |
Step 4: 검색 — "주문 생성"
사용자가 "주문 생성"을 검색합니다. 검색어도 동일한 Analyzer를 거칩니다.
"주문 생성" → Analyzer → ["주문", "생성"]
역인덱스에서 각 토큰을 조회합니다.
"주문" → [Doc 1, Doc 2, Doc 3]
"생성" → [Doc 1, Doc 3]
두 결과의 교집합을 구합니다.
[Doc 1, Doc 2, Doc 3] ∩ [Doc 1, Doc 3] = [Doc 1, Doc 3]
┌──────────────────────────────────────────────────────────────┐
│ 검색 흐름: "주문 생성" │
├──────────────────────────────────────────────────────────────┤
│ │
│ 검색어: "주문 생성" │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Analyzer │ │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ 토큰: ["주문", "생성"] │
│ │ │
│ ├──── "주문" ──▶ 역인덱스 조회 ──▶ [Doc1, Doc2, Doc3] │
│ │ │
│ └──── "생성" ──▶ 역인덱스 조회 ──▶ [Doc1, Doc3] │
│ │
│ ∩ (교집합) │
│ │ │
│ ▼ │
│ 결과: [Doc1, Doc3] │
└──────────────────────────────────────────────────────────────┘
결과는 Doc 1("주문 생성이 완료되었습니다")과 Doc 3("주문 생성 후 결제가 진행됩니다")입니다. Doc 2는 "주문"은 포함하지만 "생성"은 포함하지 않으므로 제외됩니다.
이제 두 방식의 차이가 명확해집니다.
| 방식 | 동작 | 시간 복잡도 | 문서 1억 건일 때 |
|---|---|---|---|
RDB LIKE '%주문 생성%' | 모든 문서를 순회하며 문자열 포함 여부 확인 | O(N) | 1억 건 전부 스캔 |
| 역인덱스 | "주문", "생성" 토큰을 역인덱스에서 직접 조회 | O(1)에 가까움 | 토큰 2개만 조회 |
문서가 늘어날수록 격차는 벌어집니다. 역인덱스 조회는 전체 문서 수와 거의 무관하게 동작합니다. "주문"이라는 토큰이 역인덱스에 있는지 찾는 것은, 문서가 100건이든 1억 건이든 같은 속도입니다.
이것이 Elasticsearch가 전문 검색에 강한 근본적인 이유입니다.
그런데 의문이 생길 수 있습니다. "토큰을 역인덱스에서 찾는 것이 왜 O(1)에 가까운가?" 이 질문의 답은 5편에서 다루는 FST(Finite State Transducer)라는 자료구조에 있습니다. 지금은 "사전에서 단어를 찾는 것처럼, 토큰의 위치를 바로 알 수 있는 구조"라고 이해하면 충분합니다.
핵심 정리: 역인덱스는 "단어 → 문서" 방향의 인덱스입니다. 풀 스캔 없이, 검색어를 토큰으로 분해하고 역인덱스에서 직접 조회하기 때문에 대량의 텍스트 데이터에서도 빠른 검색이 가능합니다.
역인덱스가 Elasticsearch의 검색 원리라면, 이 섹션에서는 역인덱스 위에 어떤 기능들이 쌓여 있는지를 살펴봅니다. 단순히 "빠른 검색" 하나가 아니라, 여러 기능이 결합되어 있습니다.
3장에서 역인덱스를 설명할 때 Analyzer가 잠깐 등장했습니다. Analyzer는 텍스트를 토큰으로 분해하는 컴포넌트인데, 이것이 전문 검색의 핵심입니다.
실무 예시를 하나 봅니다. 쇼핑몰 운영팀에서 "재고가 부족합니다"로 검색했을 때, "재고 부족"이 포함된 로그도 찾고 싶습니다.
저장된 문서: "재고 부족으로 주문이 실패했습니다"
검색어: "재고가 부족합니다"
이 둘은 문자열이 완전히 다릅니다. RDB의 LIKE 검색으로는 매칭되지 않습니다.
하지만 Elasticsearch에서는 매칭됩니다. 왜일까요?
저장 시 Analyzer: "재고 부족으로 주문이 실패했습니다"
→ ["재고", "부족", "주문", "실패"]
검색 시 Analyzer: "재고가 부족합니다"
→ ["재고", "부족"]
역인덱스 매칭: "재고" ✅ "부족" ✅ → 매칭 성공
저장할 때와 검색할 때 같은 Analyzer를 적용하기 때문입니다. 조사("가", "으로"), 어미("합니다", "했습니다")가 제거되고 핵심 의미만 남은 토큰끼리 비교하므로, 표현이 달라도 같은 의미의 문서를 찾을 수 있습니다.
Analyzer의 내부 동작(Char Filter → Tokenizer → Token Filter 파이프라인)과 한국어 형태소 분석기(Nori)는 3편에서 상세히 다룹니다.
RDB의 검색 결과는 "있다/없다"의 이진 판단입니다. WHERE message LIKE '%주문%'은 조건에 맞으면 반환하고, 아니면 빼는 것이 전부입니다.
Elasticsearch는 다릅니다. 각 문서에 관련도 점수(_score) 를 매기고, 점수가 높은 순으로 결과를 반환합니다.
"주문 생성"을 검색했을 때를 봅니다.
| 문서 | 내용 | _score |
|---|---|---|
| Doc 3 | "주문 생성 후 결제가 진행됩니다" | 3.2 |
| Doc 1 | "주문 생성이 완료되었습니다" | 3.1 |
| Doc 2 | "재고 부족으로 주문이 실패했습니다" | 0.8 |
Doc 1과 Doc 3은 "주문"과 "생성"을 모두 포함하므로 높은 점수를 받습니다. Doc 2는 "주문"만 포함하므로 점수가 낮습니다.
이 점수는 BM25라는 알고리즘으로 계산되는데, 크게 세 가지 요소가 영향을 줍니다.
| 요소 | 의미 | 예시 |
|---|---|---|
| TF (Term Frequency) | 해당 문서에서 토큰이 몇 번 등장하는가 | "주문"이 3번 나오면 1번보다 높은 점수 |
| IDF (Inverse Document Frequency) | 전체 문서에서 얼마나 희귀한 토큰인가 | "주문"은 흔하므로 낮은 가중치, "타임아웃"은 희귀하므로 높은 가중치 |
| 문서 길이 | 짧은 문서에 등장할수록 높은 점수 | 같은 토큰이라도 3줄짜리 문서에 나오면 30줄짜리보다 높은 점수 |
BM25 스코어링의 상세 동작은 4편에서 다룹니다.
Elasticsearch는 데이터를 Shard라는 단위로 분할하여 여러 노드에 분산 저장합니다.
┌──────────────────────────────────────────────────────┐
│ Index: order-logs │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Shard 0 │ │ Shard 1 │ │ Shard 2 │ │
│ │ │ │ │ │ │ │
│ │ Node A │ │ Node B │ │ Node C │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 검색 요청이 들어오면 → 3개 Shard에서 병렬로 검색 │
│ → 결과를 모아서 반환 │
└──────────────────────────────────────────────────────┘
이 구조가 왜 중요한가요? 데이터가 10억 건이라도, 3개의 Shard로 나눠져 있으면 각 Shard는 약 3.3억 건만 담당합니다. 3개의 노드가 병렬로 검색하므로 단일 노드 대비 처리량이 올라갑니다. 데이터가 더 늘어나면 Shard(와 노드)를 추가하면 됩니다. 이것이 수평 확장(Horizontal Scaling) 입니다.
Shard의 내부 구조와 라우팅 방식은 2편에서 상세히 다룹니다.
Elasticsearch에 문서를 저장하면, 기본적으로 약 1초 뒤에 검색이 가능해집니다.
이것을 Near Real-Time이라고 부릅니다. "완전한" 실시간이 아니라 "거의" 실시간인 이유가 있습니다. 새로 저장된 문서가 검색 가능한 상태가 되려면, 메모리 버퍼에 있던 데이터가 Segment라는 불변 구조로 만들어져야 합니다. 이 과정(refresh)이 기본 1초 간격으로 발생합니다.
문서 저장 ──▶ 메모리 버퍼 ──(1초 간격 refresh)──▶ Segment 생성 ──▶ 검색 가능
이렇게 하면 무슨 일이 생기나요? 문서를 저장한 직후에 검색하면 아직 나오지 않을 수 있습니다. 1초는 대부분의 사용 사례에서 무시할 수 있는 지연이지만, RDB의 INSERT 직후 SELECT로 바로 확인할 수 있는 것과는 다릅니다.
이것은 Elasticsearch의 의도된 설계입니다. 매 문서 저장마다 즉시 검색 가능하게 만들면 Segment가 과도하게 생성되어 성능이 떨어집니다. 약간의 지연을 허용하는 대신 전체 시스템의 성능을 유지하는 트레이드오프입니다.
Segment와 refresh의 상세 동작은 2편에서 다룹니다.
Elasticsearch는 전문 검색 외에도 다양한 검색 방식을 지원합니다.
| 쿼리 종류 | 용도 | 예시 |
|---|---|---|
| 전문 검색 (Match Query) | 텍스트를 분석하여 의미 기반 매칭 | "재고 부족"으로 검색해서 관련 로그 찾기 |
| 정확 매칭 (Term Query) | 토큰 수준에서 정확히 일치하는 문서 검색 | status: "FAILED" |
| 범위 검색 (Range Query) | 숫자, 날짜 범위 조건 | createdAt: 2025-02-21 ~ 2025-02-22 |
| 오타 허용 (Fuzzy Query) | 편집 거리 기반 유사 검색 | "OrdeService" → "OrderService" |
| 구문 검색 (Phrase Query) | 토큰의 순서와 연속성까지 매칭 | "주문 생성"이 연속으로 나타나는 문서만 |
| 복합 검색 (Boolean Query) | 여러 조건을 AND/OR/NOT으로 조합 | status = FAILED AND message에 "timeout" 포함 |
이 중에서 가장 자주 쓰이는 것은 Boolean Query입니다. 실무에서는 단일 조건으로 검색하는 경우가 드물고, 여러 조건을 조합하여 검색하기 때문입니다. 각 쿼리의 내부 동작은 4편에서 다룹니다.
핵심 정리: Elasticsearch의 강점은 하나가 아닙니다. Analyzer 기반 전문 검색, BM25 관련도 스코어링, Shard 기반 분산 처리, NRT 검색, 다양한 쿼리 타입이 결합되어 있습니다.
이번 편에서는 Elasticsearch의 정체, 핵심 용어, 역인덱스의 원리, 그리고 주요 기능을 살펴봤습니다.
Elasticsearch를 처음 접하면 "빠른 검색 엔진"이라는 인상만 남기 쉽습니다. 하지만 이 글에서 확인한 것처럼, 핵심은 데이터를 저장하는 방식 자체가 다르다는 점입니다. RDB가 행 단위로 데이터를 쌓고 인덱스로 찾아가는 구조라면, Elasticsearch는 저장하는 순간 텍스트를 분해하고 역인덱스를 만들어 검색에 최적화된 형태로 보관합니다. "빠르다"는 결과가 아니라, "다르게 저장한다"는 원인을 이해하는 것이 출발점입니다.
그런데 아직 깊이 들어가지 못한 부분이 있습니다. Index 안은 실제로 어떻게 생겼을까요? 문서가 저장되면 물리적으로 어디에, 어떤 형태로 존재하는 걸까요?
2편에서는 이 질문에 답합니다.
┌─────────────────────────────────────────────┐
│ Index │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Shard 0 │ │ Shard 1 │ │
│ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │Segment 0│ │ │ │Segment 0│ │ │
│ │ ├─────────┤ │ │ ├─────────┤ │ │
│ │ │Segment 1│ │ │ │Segment 1│ │ │
│ │ ├─────────┤ │ │ └─────────┘ │ │
│ │ │Segment 2│ │ │ │ │
│ │ └─────────┘ │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ Index > Shard > Segment 계층 구조 │
└─────────────────────────────────────────────┘
각 계층이 왜 존재하는지, Shard 수가 왜 변경 불가능한지, Segment가 불변이면 삭제와 수정은 어떻게 하는지. 2편에서 하나씩 풀어갑니다.