인덱스를 샤드로 나누면 데이터를 분산할 수 있습니다. 샤드의 레플리카를 만들면 가용성을 높일 수 있습니다.
인덱스를 샤드로 나누는 이유
Elasticsearch에서 인덱스(Index)는 관계형 데이터베이스의 테이블에 대응하는 문서들의 논리적 집합입니다. 데이터가 테라바이트(TB)나 페타바이트(PB) 규모로 늘면 단일 서버의 스토리지와 입출력(I/O) 대역폭을 넘어섭니다. 그래서 하나의 인덱스를 여러 독립 조각인 샤드(Shard)로 나눠 클러스터 전체에 분산합니다.
샤드는 데이터를 분산하는 기본 단위이면서, 그 자체로 완결된 Apache Lucene 인덱스 인스턴스입니다. 물리적으로 이 Lucene 인덱스는 다시 여러 세그먼트(Segment)로 구성됩니다. 문서가 색인될 때마다 새 세그먼트가 디스크에 쓰이고, 백그라운드 병합(Merge)이 작은 세그먼트를 큰 세그먼트로 합칩니다.
이 구조에는 물리적 상한이 있습니다. 단일 Lucene 인덱스는 내부 정수 식별자 한계 때문에 최대 2,147,483,519개 문서까지만 담을 수 있습니다(Lucene 인덱스의 구조적 상한). 따라서 데이터를 한 샤드에 몰지 않고 여러 샤드로 나누는 일이 용량 설계의 출발점입니다.
프라이머리 샤드와 레플리카 샤드
Elasticsearch는 노드 장애에 대비해 프라이머리 샤드(Primary Shard)를 레플리카 샤드(Replica Shard)로 복제합니다. 프라이머리와 레플리카는 같은 물리 노드에 배치하지 않습니다. 한 서버가 중단되면 다른 노드의 레플리카가 프라이머리로 승격됩니다.
레플리카는 장애 대비 복제본이면서 읽기 부하도 나눠 받습니다. 검색 같은 읽기 요청은 프라이머리와 레플리카에서 병렬로 처리됩니다. 그래서 레플리카 수를 늘리면 동시 검색 처리량이 함께 늘어납니다.
| 항목 | 프라이머리 샤드 | 레플리카 샤드 |
|---|---|---|
| 맡는 일 | 원본 데이터 저장 | 복제본, 읽기 분산 |
| 개수 변경 | 인덱스 생성 후 고정 | 운영 중 동적 조정 |
| 쓰기 | 직접 수행 | 프라이머리에서 복제 |
| 배치 규칙 | 노드에 분산 | 프라이머리와 다른 노드 |
레플리카 수는 읽기 부하에 맞춰 운영 중 늘리거나 줄일 수 있습니다. 프라이머리 수는 인덱스를 만들 때 고정합니다. 다음 절에서 설명하는 라우팅 방식 때문입니다.
문서가 샤드를 찾는 방법
특정 문서가 어느 샤드에 저장될지는 라우팅(Routing) 공식이 결정합니다. 프라이머리 개수가 해싱 함수와 결합되어 고정되는 이유가 여기서 드러납니다.
shard = hash(routing) % number_of_primary_shardsrouting 값은 기본적으로 문서의 _id입니다. 이를 해시한 뒤 프라이머리 개수로 나눈 나머지가 목적지 샤드입니다. 나머지 연산이 프라이머리 개수에 직접 의존하므로, 개수가 바뀌면 기존 문서의 목적지가 어긋납니다. 그래서 프라이머리 개수는 생성 후 변경할 수 없습니다.
검색은 반대로 동작합니다. 코디네이팅 노드가 관련 샤드에 질의를 분산(Scatter)하고, 각 샤드의 부분 결과를 병합(Gather)해 최종 응답을 만듭니다.
flowchart LR
C[클라이언트<br/>검색 요청] --> CO[코디네이팅 노드<br/>분산과 병합 담당]
CO -->|Scatter| S0[샤드 0<br/>프라이머리]
CO -->|Scatter| S1[샤드 1<br/>레플리카]
CO -->|Scatter| S2[샤드 2<br/>프라이머리]
S0 -->|Gather| CO
S1 -->|Gather| CO
S2 -->|Gather| CO
CO --> R[최종 응답<br/>정렬·병합 결과]
그림은 검색 요청을 여러 샤드로 나누고 결과를 한 노드에서 병합하는 과정을 보여줍니다. 인덱싱은 라우팅으로 한 샤드를 선택하고, 검색은 여러 샤드의 결과를 모읍니다. 따라서 샤드 설계는 읽기와 쓰기에 서로 다른 영향을 줍니다.
쓰기가 복제되는 순서
쓰기는 항상 프라이머리를 먼저 거칩니다. 코디네이팅 노드가 라우팅으로 목적지 프라이머리를 선택하면, 프라이머리가 자기 샤드에 먼저 색인합니다. 이후 같은 데이터를 레플리카로 복제합니다. 복제를 확인하면 클라이언트에 성공을 응답합니다.
flowchart LR
W[클라이언트<br/>색인 요청] --> CO[코디네이팅 노드<br/>라우팅으로 목적지 선택]
CO --> P[프라이머리 샤드<br/>로컬 색인 먼저 수행]
P -->|복제| R1[레플리카 샤드 1]
P -->|복제| R2[레플리카 샤드 2]
R1 -->|확인| P
R2 -->|확인| P
P -->|성공 응답| W
위 그림은 쓰기가 프라이머리에서 레플리카 순서로 전파되는 경로입니다(Elasticsearch 표준 복제 동작). 그래서 레플리카가 많으면 한 번의 쓰기가 더 많은 노드로 퍼져 쓰기 비용이 늘어납니다. 읽기 처리량과 쓰기 비용이 레플리카 개수를 사이에 두고 맞물립니다.
노드 역할 분리
클러스터(Cluster)는 여러 노드를 하나의 논리 시스템으로 묶은 단위입니다. 각 노드는 부여된 역할에 따라 다른 일을 맡습니다.
| 노드 역할 | 맡는 일 |
|---|---|
| 마스터 노드 | 클러스터 상태와 메타데이터, 샤드 할당·재배치 관리 |
| 데이터 노드 | 문서와 역색인 저장, 검색·집계·인덱싱 처리 |
| 인제스트 노드 | 색인 직전 변환·보강·필터링 전처리 |
| 코디네이팅 노드 | 요청 분산과 응답 병합, 로드 밸런서 |
마스터 노드는 샤드 할당과 클러스터 상태를 관리하므로 데이터 입출력 부하에서 분리하는 편이 안정적입니다. 데이터 노드는 검색과 집계처럼 CPU와 메모리를 많이 사용하는 작업을 처리합니다. 작은 환경에서는 한 노드가 여러 역할을 맡을 수 있습니다. 운영 환경에서는 마스터와 데이터 부하를 분리하는 편이 장애에 강합니다.
샤드 개수를 정하는 기준
샤드는 많아도 적어도 문제가 됩니다. 너무 적으면 단일 샤드가 비대해지고, 너무 많으면 관리 비용이 불어납니다.
Elastic은 일반 지침으로 샤드당 10~50GB와 2억 문서 미만을 제시하지만 워크로드에 따라 달라질 수 있다고 설명합니다(Elastic 샤드 크기 문서). 예시로 300GB를 프라이머리 10개에 균등하게 나누면 샤드당 약 30GB입니다. 실제 배치는 노드 수, 라우팅, 복제본 설정을 함께 반영해야 합니다.
이 결정은 인덱스를 만들 때 두 설정으로 표현됩니다. number_of_shards는 프라이머리 개수라 생성 후 바꿀 수 없고, number_of_replicas는 운영 중 갱신할 수 있습니다.
다음 예시는 Elasticsearch Console의 HTTP 요청 형식입니다.
PUT /logs-2026-06
{
"settings": {
"number_of_shards": 10,
"number_of_replicas": 1
}
}레플리카를 1로 두면 프라이머리 10개에 레플리카 10개가 더해져 전체 20개 샤드가 됩니다. 레플리카 값은 나중에 PUT /logs-2026-06/_settings로 올리거나 내릴 수 있습니다(Elasticsearch 표준 인덱스 설정).
샤드가 지나치게 많아지는 문제를 샤드 폭발(Shard Explosion)이라고 합니다. 데이터가 적어도 매일 새 인덱스를 만들고 정리하지 않으면 불필요한 샤드가 수백 개로 늘어납니다. 각 샤드는 독립적인 Lucene 인스턴스이므로 CPU, JVM(Java Virtual Machine) 힙, 디스크 I/O를 사용합니다. 이 비용 때문에 클러스터 상태 갱신과 노드 재시작이 느려집니다.
시계열 로그처럼 계속 쌓이는 데이터는 인덱스 수명 주기 관리(ILM, Index Lifecycle Management)로 다룹니다. 일정 크기나 기간마다 인덱스를 새로 굴리고(rollover), 오래된 인덱스는 핫-웜-콜드 단계로 옮겨 비용을 낮춥니다. 이렇게 하면 한 인덱스의 샤드가 무한정 커지지 않습니다.
리밸런싱과 장애 동작
샤드가 추가되거나 노드가 빠지면 클러스터는 샤드를 재배치합니다. 리밸런싱(Rebalancing)은 노드 사이로 데이터를 옮기므로 네트워크와 디스크를 많이 사용합니다. 복구 중 이 작업이 과도하면 성능이 급격히 떨어질 수 있습니다.
장애 시 동작은 앞의 설계에서 그대로 따라옵니다. 노드가 죽어 프라이머리가 사라지면 다른 노드의 레플리카가 프라이머리로 승격됩니다. 반면 마스터가 불안정하면 샤드 할당 자체가 흔들려 클러스터 전체 운영 문제로 번집니다.
그래서 샤드와 레플리카 설계는 평상시 성능만이 아니라 장애 복구 비용까지 함께 봐야 합니다. 레플리카가 없으면 프라이머리 손실이 곧 데이터 유실입니다. 레플리카가 지나치게 많으면 쓰기와 저장 비용이 늘고, 복구할 때 옮길 데이터도 많아집니다.
정리
Elasticsearch의 샤딩 아키텍처는 인덱스를 샤드로 나누는 결정과 그 샤드를 레플리카로 복제하는 결정이 맞물린 구조입니다. 프라이머리 개수는 라우팅 해시에 묶여 생성 후 고정되고, 레플리카 개수는 읽기 부하에 맞춰 운영 중 조정됩니다. 노드 역할을 분리하면 마스터의 통제 부하와 데이터 노드의 입출력 부하가 섞이지 않습니다. 샤드 크기를 30~50GB로 잡고 샤드 폭발과 과도한 리밸런싱을 피하는 일이 안정적인 클러스터 운영의 기준선입니다.