샤드가 복제될 때 세그먼트도 같이 복제될까?

2026년 07월 23일

opensearch

시리즈OpenSearch 내부 구조#6
# OpenSearch# 복제# 샤드# 세그먼트# Replication
목차

들어가며

시리즈를 여기까지 따라왔다면 이제 이런 그림이 그려질 겁니다.

  • 샤드 하나는 Lucene 인덱스 하나이고, 노드 하나 안에 통째로 있다 (1편)
  • 그 안에는 세그먼트들이 들어있고, 세그먼트는 불변이다 (2·4편)
  • refresh를 해야 새 세그먼트가 만들어지고 검색된다 (3·5편)

그런데 샤드는 복제됩니다. primary가 있고 replica가 있죠. 여기서 자연스러운 질문이 나옵니다.

샤드가 복제되면, 그 안의 세그먼트도 똑같이 복제되는 걸까?

결론부터 말하면 기본 설정에서는 복제되지 않습니다. primary와 replica는 각자 자기 세그먼트를 따로 만듭니다. 이 차이가 검색 결과가 튀는 현상, 디스크 사용량 불일치, 색인 처리량까지 전부 설명해줍니다.

ℹ️

이번 글의 범위

  • 다루는 것: Document Replication과 Segment Replication의 차이, 그로 인한 실제 현상, 선택 기준
  • 다루지 않는 것: 샤드 개수 설계, 복제본 수 튜닝 (3차 시리즈에서 다룸)

질문을 정확히 다시 쓰기

1편에서 “샤드는 노드에 걸쳐 있지 않다”고 했습니다. 샤드 하나 = Lucene 인덱스 하나이고, 그건 자기완결적이라 물리적으로 쪼갤 수 없기 때문이었죠.

그래서 “세그먼트가 여러 노드에 분산되나?”라는 질문은 성립하지 않습니다. 진짜 질문은 이겁니다.

primary 샤드의 세그먼트와 replica 샤드의 세그먼트는 같은 파일인가?

[P0] Node A          [R0] Node B
├── _0.cfs           ├── ???
├── _1.cfs           ├── ???
└── segments_5       └── ???
                          ↑ 왼쪽과 같은 파일일까?

Document Replication — 세그먼트는 복제되지 않는다

OpenSearch의 기본 복제 전략입니다. 이름 그대로 복제되는 단위가 문서예요.

primary가 문서를 색인하면, primary는 완성된 세그먼트를 보내는 게 아니라 문서 그 자체를 replica에 전달합니다. replica는 그 문서를 받아서 처음부터 자기가 다시 색인합니다.

문서를 그대로 전달refresh (1s)refresh (1s)primary와 무관하게 각자이름·개수·크기 전부 다름색인 요청Primary 샤드buffer + translogReplica 샤드buffer + translog_5.cfs 생성_3.cfs 생성

핵심은 refresh도 merge도 primary와 replica가 각자 독립적으로 돌린다는 것입니다. 같은 문서를 받았어도 언제 세그먼트로 굳힐지, 언제 merge할지는 각 복사본이 알아서 결정해요.

결과적으로 이렇게 됩니다.

관점primary vs replica
논리적 문서 집합(결국) 동일 ✅
세그먼트 파일 이름·개수다름 ❌
세그먼트 파일 크기다름 ❌
디스크 사용량다를 수 있음 ❌
색인 CPUreplica 수만큼 중복 소모

replica가 2개면 똑같은 색인 작업을 클러스터 전체에서 3번 하는 셈입니다. 이게 document replication의 가장 큰 비용이에요.


이것 때문에 생기는 실제 현상

① 검색 결과 순서가 튄다 (bouncing results)

사용자가 새로고침했는데 검색 결과 순서가 미묘하게 바뀌는 걸 본 적 있다면, 원인이 대부분 여기입니다.

관련도 점수는 샤드 단위 통계(문서 수, 단어 빈도)로 계산됩니다. 그런데 4편에서 봤듯 삭제된 문서는 merge 전까지 파일에 남아있고, 통계에도 잡힙니다. merge 타이밍이 primary와 replica마다 다르니 통계도 미묘하게 달라져요.

1번째 요청2번째 요청정렬 순서 뒤바뀜동일한 검색 쿼리라우팅Primary(merge 완료, 묘비 정리됨)Replica(merge 전, 묘비 잔존)score 8.42score 8.39

해결책은 같은 사용자를 항상 같은 복사본으로 보내는 것입니다.

GET /products/_search?preference=user_12345
{
  "query": { "match": { "name": "무선 마우스" } }
}

preference 값이 같으면 항상 같은 복사본으로 라우팅됩니다. 세션 ID나 사용자 ID를 넣는 게 일반적이에요.

② 방금 넣은 문서가 보였다 안 보였다 한다

5편에서 NRT를 다뤘는데, 복제까지 고려하면 한 겹 더 복잡해집니다. refresh 타이밍이 복사본마다 다르기 때문이에요.

t=0.0s  문서 색인 (primary, replica 모두 buffer에 있음)
t=0.7s  primary refresh   → primary로 가면 검색됨 ✅
t=1.0s  검색 요청 → replica로 라우팅 → 안 나옴 ❌
t=1.3s  replica refresh   → 이제 나옴 ✅

같은 시각에 같은 쿼리를 던져도 어느 복사본으로 가느냐에 따라 결과가 다릅니다. 5편에서 본 refresh=wait_for나 ID 조회가 필요한 이유가 하나 더 늘어난 셈이죠.

③ primary와 replica의 디스크 사용량이 다르다

merge 타이밍이 달라서 생깁니다. 모니터링 대시보드에서 “같은 샤드인데 크기가 왜 다르지?”를 보게 되는데, 정상입니다.


Segment Replication — 세그먼트 파일 자체가 복사된다

OpenSearch 2.7에서 정식 기능이 된 전략입니다. 복제 단위가 세그먼트 파일이에요.

primary만 색인을 하고, 완성된 세그먼트 파일을 replica가 가져갑니다.

refresh세그먼트 파일 전송(바이트 단위로 동일)색인 요청Primary 샤드(혼자 색인)_5.cfs 생성_5.cfsReplica 샤드(색인 CPU 0)

여기서는 primary와 replica의 세그먼트가 바이트 단위로 동일합니다. 진짜로 “세그먼트가 복제”되는 거죠.

💡

이게 가능한 건 세그먼트가 불변이기 때문입니다.

4편에서 “불변성의 대가” 표에 “세그먼트 파일 자체를 복사·캐싱 가능”이라고 적었던 게 이겁니다.

파일이 계속 변한다면 전송 도중에 내용이 바뀌어서 복사가 불가능해요. 절대 안 바뀌기 때문에 그냥 통째로 보내면 되고, 받은 쪽은 그대로 열기만 하면 됩니다.

얻는 것

  • replica가 색인 CPU를 전혀 쓰지 않음
  • 초기 벤치마크 기준 동일 구성에서 처리량 약 40% 향상 보고
  • merge도 primary만 수행 → 클러스터 전체 자원 절약

치르는 대가

  • 네트워크 사용량이 크게 늘어남. 문서는 작지만 세그먼트 파일은 큽니다. merge로 만들어진 수 GB짜리 세그먼트도 통째로 전송해야 해요
  • replication lag이 생깁니다. replica의 검색 가시성이 primary보다 뒤처집니다
  • replica도 안전을 위해 translog는 계속 유지합니다

즉 병목이 CPU에서 네트워크로 옮겨가는 트레이드오프입니다.


한눈에 비교

항목Document ReplicationSegment Replication
복제 단위문서세그먼트 파일
replica의 색인직접 수행 (중복 CPU)안 함
세그먼트 물리 형태복사본마다 다름동일
주 병목CPU네트워크
색인 처리량기준약 40% 향상
read-after-write상대적으로 강함replication lag 있음
기본값현재 기본명시 설정 필요

어떻게 고를까

YesNoNoYesNoYes색인이 무겁고replica가 많은가?네트워크가이미 병목인가?Document Replication(기본 유지)색인 직후 즉시검색되어야 하나?Segment Replication

  • 로그·이벤트처럼 색인은 무겁고 실시간성이 덜한 데이터 → segment replication이 유리
  • 주문·재고처럼 색인 직후 바로 읽어야 하는 데이터 → document replication 유지
  • 네트워크가 이미 빡빡한 클러스터 → segment replication은 오히려 독

설정 방법

PUT /my-index
{
  "settings": {
    "index.replication.type": "SEGMENT"
  }
}
⚠️

인덱스를 만들 때만 지정할 수 있습니다.

이미 만들어진 인덱스의 복제 전략은 바꿀 수 없어요. 전환하려면 새 인덱스를 만들고 reindex한 뒤 alias를 갈아끼워야 합니다.

→ 무중단 인덱스 교체 전략 참고

현재 기본값은 여전히 DOCUMENT입니다. 3.0 이후 SEGMENT를 기본으로 바꾸자는 제안이 논의 중이에요.


한 걸음 더: Remote-backed Storage

OpenSearch 2.10부터는 세 번째 선택지가 있습니다. 세그먼트와 translog를 S3 같은 외부 저장소에 올리고, replica가 거기서 내려받는 방식이에요.

업로드다운로드다운로드다운로드PrimaryRemote Store(S3)Replica 1Replica 2Replica 3

primary가 replica마다 따로 전송하던 부담이 사라지고, 데이터 안전성을 스토리지에 위임합니다. replica가 많을수록 이득이 커집니다.


정리

  1. 기본 설정에서 세그먼트는 복제되지 않는다. 문서가 복제되고, primary와 replica가 각자 refresh·merge를 돌려 물리적으로 다른 세그먼트를 만든다.
  2. 그래서 검색 점수가 미묘하게 튀고(→ preference), refresh 타이밍도 어긋나며, 디스크 사용량도 복사본마다 다를 수 있다.
  3. Segment Replication을 켜야 세그먼트 파일 자체가 복사된다. 색인 CPU를 아끼는 대신 네트워크와 지연을 지불한다.
  4. 이게 가능한 이유도 결국 세그먼트가 불변이기 때문이다.

시리즈를 마치며

여섯 편을 관통한 이야기는 사실 한 문장이었습니다.

세그먼트는 불변이다.

  • 불변이라서 → 읽기에 락이 필요 없고 (4편)
  • 불변이라서 → 수정·삭제를 못 해 묘비와 merge가 필요하고 (4편)
  • 불변이라서 → 새로 만들어야 보이니 Near Real-Time이고 (5편)
  • 불변이라서 → 파일을 그대로 복사하는 segment replication이 가능하다 (6편)

따로 외울 것 같았던 네 가지가 전부 같은 뿌리에서 나옵니다. 이걸 잡고 있으면 앞으로 만날 대부분의 동작도 설명이 됩니다.

다음 시리즈에서는 이 구조 위에서 검색이 실제로 어떻게 동작하는지 — 형태소 분석, 쿼리, 관련도 점수, 매핑 설계를 다룹니다.

🤔기본 설정(Document Replication)에서 primary와 replica의 세그먼트 파일은 어떤 관계일까요?

참고자료

© 2026, 미나리와 함께 만들었음