학습 목표
Elasticsearch가 분산 검색·분석 엔진으로 무엇을 하는지 설명할 수 있다.
document·index·_id·_source의 관계를 설명할 수 있다.
inverted index가 full-text 검색에 쓰이는 이유를 설명할 수 있다.
near real-time(NRT) 에서 index 직후 검색이 즉시 되지 않는 이유를 설명할 수 있다.
기본 Index·Get·Search REST API로 document를 넣고 조회할 수 있다.
문제 상황
- JSON 로그를 Elasticsearch에 넣었는데 Kibana에서 안 보인다 — refresh 전이라 NRT 타이밍
orders테이블처럼 생각하고UPDATE했더니 새 document가 생김 — document 불변·append 모델message필드만 검색하면 되는데 모든 필드를 keyword로 잡아 디스크가 터진다 — mapping은 3편GET /logs/_search결과에_score가 있는데 정렬 기준이 뭔지 모르겠다- RDB
LIKE '%error%'는 풀 스캔 — ES는 inverted index로 빠르게 찾는다고 하는데 구조를 모름 - 오리엔테이션에서 index·search 축만 봤고, document가 어떻게 저장·검색되는지는 처음
앞 편에서 Elastic Stack 데이터 흐름을 봤다. 이번 편은 Elasticsearch 코어 — document·index·inverted index·NRT. mapping·analyzer는 3편, Query DSL은 4편이다.
1. Elasticsearch란?
- Elasticsearch (ES)
- 분산 document 저장소 + full-text 검색 + 집계(aggregation) 엔진
- Apache Lucene 위에 REST API·클러스터 조율·운영 기능을 얹은 Elastic Stack의 중심
- 주 용도
- 로그·이벤트 검색 (ELK의 L 저장소)
- 앱 검색, 메트릭·APM 데이터, 보안 이벤트(SIEM)
- RDB와 다른 점
- 스키마는 mapping으로 유연(동적 mapping 가능) — 3편
- JOIN은 제한적 — denormalize·nested·parent-child는 특수 케이스
- 트랜잭션 단위가 document — row 단위 ACID와 다름
| RDB | Elasticsearch | |
|---|---|---|
| 단위 | row | document (JSON) |
| 조회 | SQL | Query DSL / KQL |
| full-text | LIKE, full scan | inverted index |
| 스케일 | 수직·샤딩 수동 | shard·replica 기본 (5편) |
| 업데이트 | in-place UPDATE | reindex·update API (내부는 새 버전) |
2. Document와 Index
데이터는 JSON document로 저장된다.

Document
- document — ES가 인덱싱·검색하는 최소 단위 (JSON 객체)
_id— document 식별자 (직접 지정 또는 자동 생성)_source— 원본 JSON 본문- metadata —
_index,_id,_version,_score(검색 시) 등
{
"@timestamp": "2026-07-18T06:00:01.123Z",
"level": "ERROR",
"service": "order-api",
"message": "payment timeout",
"order_id": 9001,
"trace_id": "a1b2c3d4e5f6"
}
- 로그·이벤트는 한 줄(한 이벤트) = 한 document가 자연스럽다
- 앞서 구조화 로그 필드(
level,trace_id)가 그대로 ES 필드가 됨
Index
- index — document의 논리적 컬렉션 (RDB table에 대응하기 쉽지만 동일하지 않음)
- 이름 예:
logs-order-prod,metrics-k8s-2026.07 - 한 document는 한 index에 속함 (
_index메타데이터) - 물리적으로 index는 shard로 나뉨 — 5편에서 cluster·routing
index: app-logs
├── doc _id=1 { level: INFO, message: "started" }
├── doc _id=2 { level: ERROR, message: "timeout" }
└── doc _id=3 { level: WARN, message: "retry" }
- index vs data stream — 최신 로그는 data stream 권장 (9편). 개념은 동일하게 document 집합
3. Inverted Index
full-text 검색의 핵심 자료구조.

동작
- 정방향 — document → 필드 텍스트
- 역방향(inverted) — term(단어) → 어떤 document에 있는지 posting list
Doc 1: "payment timeout error"
Doc 2: "payment succeeded"
Doc 3: "timeout retry"
term → doc ids
payment → 1, 2
timeout → 1, 3
error → 1
succeeded → 2
retry → 3
- 검색
timeout— posting list {1, 3} 즉시 조회 — 전체 document 스캔 불필요 - analyzer가 텍스트를 term으로 쪼갬 — 소문자·형태소·동의어 (3편)
text— analyzed, full-text 검색용keyword— analyzed 안 함, 정확 일치·필터·집계용 (3편)
| 질문 | inverted index |
|---|---|
| "error 포함 로그" | term error posting |
| "payment AND timeout" | 두 posting list 교집합 |
| order_id=9001 | keyword/long 필드 — B-tree 유사 doc values |
- 집계(aggregation) 는 often doc values (columnar) 사용 — 5편
4. Near Real-Time 검색
index API 직후 바로 검색 안 될 수 있다 — NRT 모델.

Index request
→ in-memory buffer (빠른 쓰기)
→ refresh (기본 ~1s) → new segment (searchable)
→ eventual merge to larger segments
| 단계 | |
|---|---|
| Index | document를 메모리 버퍼에 기록 — 아직 검색 불가일 수 있음 |
| Refresh | 버퍼를 segment로 만들어 searchable — 기본 index.refresh_interval 1s |
| Flush | segment를 디스크에 확정 — 트랜잭션 로그 정리 |
| Merge | 작은 segment 병합 — 백그라운드, 검색은 계속 가능 |
- trade-off
- refresh 짧게 — 검색 지연↓, CPU·I/O↑
- bulk ingest 시
refresh_interval=-1로 끄고 끝에 한 번 refresh — 대량 적재 패턴
GET /index/_refresh— 수동 refresh (테스트·배치 후)- RDB commit 직후 SELECT와 달리 — 1초 내 보이면 NRT로 충분한 경우가 많음 (로그·대시보드)
5. 기본 REST API
클러스터 URL을 http://localhost:9200 으로 가정.
Index (생성·적재)
# 자동 _id
curl -X POST "localhost:9200/app-logs/_doc" \
-H "Content-Type: application/json" \
-d '{
"@timestamp": "2026-07-18T06:00:01Z",
"level": "ERROR",
"service": "order-api",
"message": "payment timeout",
"order_id": 9001
}'
# _id 지정
curl -X PUT "localhost:9200/app-logs/_doc/order-9001-error" \
-H "Content-Type: application/json" \
-d '{ ... }'
_doc— document 타입 (7.x+ 타입 제거,_doc관례)- 응답:
"result": "created"/"updated",_id,_version
Get (단건 조회)
curl "localhost:9200/app-logs/_doc/order-9001-error"
_source에 원본 JSON — 검색 없이 ID로 직접 fetch (routing·shard — 5편)
Search (검색)
curl -X POST "localhost:9200/app-logs/_search" \
-H "Content-Type: application/json" \
-d '{
"query": {
"match": { "message": "timeout" }
},
"size": 10
}'
hits.hits[]— 매칭 document +_score(관련도)match— analyzed 필드 full-text — 4편에서 bool·filter·pagination- Kibana Discover도 동일 index에 Query DSL / KQL 전송
Bulk (대량 적재)
curl -X POST "localhost:9200/_bulk" \
-H "Content-Type: application/x-ndjson" \
--data-binary @code/bulk-logs.ndjson
- 한 HTTP 요청에 여러 index — ingest 성능의 기본 (Beats·Logstash도 bulk 사용)
6. 실무에서 기억할 점
| 주제 | 이번 편 | 이후 |
|---|---|---|
| 필드 타입·analyzer | 개념만 | 3편 mapping |
| 쿼리·필터·정렬 | match 맛보기 |
4편 Query DSL |
| shard·replica·routing | index에 존재 | 5편 cluster |
| data stream | classic index 먼저 | 9편 |
| 보안·인증 | 생략 | 20편 |
- document 설계 — 검색·집계할 필드를 미리 JSON 키로 (구조화 로그)
- 고카디널리티 —
trace_id는 검색에, keyword 인덱싱 남발은 비용 (Observability·3편) - index 이름 — env·서비스·날짜 패턴 (
logs-order-prod-2026.07.18) — ILM·template과 연계 (13~14편)
정리
- Elasticsearch — JSON document 저장 + inverted index 검색 + 집계
- index — document 논리 집합; 물리적으로 shard에 분산
- inverted index — term → document 목록; full-text의 속도 근원
- NRT — refresh 후 searchable; 기본 ~1s 지연 각오
- Index / Get / Search / Bulk — 스택 전반(Beats·Kibana)의 기반 API
다음에 다룰 것
- Mapping과 Analyzer
- field type, dynamic mapping, text vs keyword, analyzer
해당 내용은 Elasticsearch Guide (Documents and indices, Near real-time search) 를 기반으로 합니다.
'ELK & Elastic Cloud' 카테고리의 다른 글
| Beats (0) | 2026.07.11 |
|---|---|
| Aggregation과 클러스터 (0) | 2026.07.08 |
| Query DSL (0) | 2026.07.05 |
| Mapping과 Analyzer (0) | 2026.07.03 |
| ELK · Elastic Cloud 오리엔테이션 (0) | 2026.06.30 |