문서 하나를 넣으면 무슨 일이 벌어질까?

2026년 07월 23일

opensearch

시리즈OpenSearch 내부 구조#3
# OpenSearch# 색인# Translog# Refresh# Flush# Merge
목차

들어가며

문서를 하나 넣습니다.

POST /products/_doc
{ "name": "무선 노트북 마우스" }

응답이 201 Created로 돌아옵니다. 성공이네요. 그런데 바로 검색해보면 안 나올 수 있습니다.

색인이 성공했는데 검색이 안 된다니 버그 같지만, 정상 동작입니다. 문서가 검색 가능해지기까지 몇 단계를 더 거쳐야 하거든요. 이번 글에서 그 단계들을 하나씩 눌러보겠습니다.

ℹ️

이번 글의 범위

  • 다루는 것: buffer → translog → refresh → flush → merge 파이프라인, 각 단계의 안전성
  • 다루지 않는 것: 왜 하필 1초 주기인지(5편), 락이 없는 이유(4편)

인쇄소로 감 잡기

1편에서 세그먼트를 “인쇄된 책” 에 비유했습니다. 그 비유를 그대로 이어가면 파이프라인 전체가 설명됩니다.

OpenSearch인쇄소검색되나?안전한가?
In-memory buffer작성 중인 원고❌❌ 정전되면 날아감
Translog원고 백업 사본❌✅ 복구 가능
refresh인쇄 → 서가에 꽂기✅ 이때부터❌ 아직 메모리
flush제본 → 서고에 영구 보관✅✅ 완전히 안전
merge얇은 책 여러 권을 합본✅✅

핵심은 “인쇄해야 읽을 수 있다” 입니다. 원고가 아무리 완성돼 있어도, 인쇄해서 서가에 꽂기 전에는 아무도 못 읽어요. 색인 요청이 성공했는데 검색이 안 되는 이유가 정확히 이겁니다.


직접 눌러보기

말로 설명하는 것보다 직접 해보는 게 빠릅니다. 아래 순서대로 눌러보세요.

🔬색인 파이프라인 시뮬레이터
📝In-memory Buffer검색 불가
비어 있음
📜Translogflush 완료
비어 있음
💾세그먼트검색 가능 · 한번 만들면 수정 불가
아직 세그먼트가 없습니다
🔍 지금 검색되는 문서
하나도 없습니다
💡 문서를 색인해보세요.

실습 1 — 색인만 해보기

  1. 문서 색인 을 3번 누릅니다
  2. 아래 “지금 검색되는 문서”를 봅니다 → 하나도 없습니다

문서를 3개나 넣었는데 검색되는 게 없죠. buffer와 translog에는 쌓였는데 세그먼트가 없기 때문입니다.

실습 2 — refresh 눌러보기

  1. refresh 를 누릅니다
  2. 세그먼트 _0이 생기고, 검색되는 문서가 3개로 바뀝니다

이게 “인쇄”입니다. 이 순간부터 검색됩니다.

실습 3 — flush와 merge

  1. 문서 색인 2번 → refresh → 세그먼트가 2개가 됩니다
  2. flush → translog가 비워집니다 (영구 보관 확정)
  3. merge → 세그먼트 2개가 1개로 합쳐집니다

단계별로 뜯어보기

이제 각 단계가 정확히 무슨 일을 하는지 봅시다.

refresh (기본 1초)flushmerge색인 요청In-memory BufferTranslog새 세그먼트검색 가능디스크 커밋translog 비움세그먼트 합치기

1단계 — buffer와 translog에 동시 기록

색인 요청이 오면 두 곳에 동시에 씁니다.

  • In-memory buffer: 메모리. 나중에 세그먼트로 만들 재료
  • Translog: 디스크에 append. 순차 쓰기라 빠름

왜 두 곳에 쓸까요? buffer는 메모리라서 장비가 꺼지면 사라지기 때문입니다. translog가 없으면 색인 성공 응답을 받은 문서가 증발할 수 있어요.

ℹ️

Translog는 왜 빠른가?

translog는 파일 끝에 계속 덧붙이기만 합니다(append-only). 디스크는 순차 쓰기가 랜덤 쓰기보다 훨씬 빠르기 때문에, “안전하게 기록하기”의 비용이 생각보다 작습니다.

역색인처럼 정렬을 유지해야 하는 구조와 정반대죠. 그래서 역할을 나눈 겁니다. 빠르게 기록하는 일은 translog가, 빠르게 검색되는 일은 세그먼트가 담당합니다.

2단계 — refresh: 검색 가능해지는 순간

buffer에 쌓인 문서들을 모아 새 세그먼트 하나를 만듭니다. 이 세그먼트가 열리는 순간부터 검색됩니다.

refresh 전:  buffer [doc4, doc5]     검색 결과: doc1, doc2, doc3
refresh 후:  buffer []               검색 결과: doc1 ~ doc5
             세그먼트 _1 [doc4, doc5]  ← 새로 생김

기본값은 1초마다 자동 실행입니다. 그래서 “넣고 1초 뒤엔 검색된다”가 성립해요.

⚠️

refresh는 아직 디스크 커밋이 아닙니다.

새 세그먼트는 만들어졌지만 아직 파일시스템 캐시에 있습니다. 지금 장비가 꺼지면 이 세그먼트는 사라져요. 그래도 데이터는 안전한데, translog가 아직 살아있기 때문입니다. 재시작하면 translog를 재생해서 복구합니다.

3단계 — flush: 영구 보관 확정

세그먼트를 디스크에 진짜로 커밋하고, translog를 비웁니다.

translog를 비울 수 있는 이유는 간단합니다. 이제 세그먼트가 디스크에 안전하게 있으니, 복구용 기록이 필요 없어진 거죠.

flush 전:  translog [doc1 ~ doc5]   ← 복구용으로 계속 보관
flush 후:  translog []              ← 비움. 세그먼트가 디스크에 있으므로 불필요

flush는 자동으로 일어납니다. translog가 일정 크기를 넘거나, 일정 시간이 지나면요.

4단계 — merge: 늘어난 세그먼트 정리

refresh가 1초마다 돌면 세그먼트가 계속 늘어납니다. 1분이면 60개, 1시간이면 3600개죠.

세그먼트가 많으면 검색할 때 그만큼 많은 파일을 뒤져야 해서 느려집니다. 그래서 백그라운드에서 작은 세그먼트들을 큰 것 하나로 합칩니다.

merge 전:  _0 [doc1, doc2]  _1 [doc3]  _2 [doc4, doc5]
merge 후:  _3 [doc1, doc2, doc3, doc4, doc5]

merge는 자동으로 돌지만, 시뮬레이터에서는 타이밍을 눈으로 보려고 버튼으로 만들어뒀습니다.

💡

merge에는 숨은 역할이 하나 더 있습니다.

삭제된 문서를 진짜로 지우는 일입니다. 세그먼트는 불변이라 문서를 삭제해도 파일에서 지울 수 없고, “삭제됨” 표시만 붙습니다. 그 표시된 문서들이 실제로 사라지는 시점이 바로 merge예요.

이 이야기는 4편에서 자세히 다룹니다.


안전성은 어느 시점부터인가

파이프라인에서 가장 헷갈리는 부분입니다. 정리하면 이렇습니다.

장비 꺼져도translog로 복구 ✅검색은 되지만세그먼트는 아직 메모리디스크 커밋 완료색인 요청buffer + translogrefreshflush안전검색 가능완전히 안전

시점검색되나장비가 꺼지면
buffer에만 있음❌✅ translog로 복구
refresh 후✅✅ translog로 복구
flush 후✅✅ 세그먼트가 디스크에 있음

“검색 가능”과 “안전”은 별개라는 게 핵심입니다. 색인 요청이 성공한 순간 이미 안전하고(translog 덕분), 검색 가능해지는 건 그보다 나중입니다(refresh 이후).


실무에서 만나는 지점

대량 색인할 때 refresh 끄기

수백만 건을 한 번에 넣을 때 1초마다 세그먼트가 만들어지면, 그 세그먼트들을 merge하느라 자원을 낭비합니다. 이럴 땐 아예 꺼버립니다.

PUT /products/_settings
{ "index.refresh_interval": "-1" }

색인이 끝나면 되돌립니다.

PUT /products/_settings
{ "index.refresh_interval": "1s" }

색인 직후 바로 읽어야 할 때

테스트 코드나 “저장 후 목록 갱신” 같은 흐름에서 필요합니다.

POST /products/_doc?refresh=wait_for
{ "name": "무선 노트북 마우스" }

wait_for는 다음 refresh가 돌 때까지 응답을 보류합니다.

🚨

refresh=true는 주의해서 쓰세요.

즉시 refresh를 강제합니다. 편하지만, 호출할 때마다 세그먼트가 하나씩 생겨서 merge 부담이 급격히 커집니다. 배치 색인 루프 안에서 쓰면 클러스터가 망가질 수 있어요.

급하지 않다면 wait_for를, 정말 급하면 true를 쓰되 호출 빈도를 반드시 확인하세요.


정리

  1. 색인 요청은 buffer와 translog에 동시에 기록된다. translog 덕분에 이 순간 이미 안전하다.
  2. refresh가 검색 가능해지는 순간이다. buffer가 새 세그먼트로 굳는다. 기본 1초 주기.
  3. flush는 디스크 커밋이다. 세그먼트가 영구 보관되고 translog가 비워진다.
  4. merge는 늘어난 세그먼트를 합친다. 삭제된 문서가 실제로 사라지는 시점이기도 하다.
  5. “검색 가능”과 “안전”은 다른 시점이다. 안전이 먼저고 검색이 나중이다.

다음 글에서는 이 모든 과정이 돌아가는 동안 왜 검색이 한 번도 멈추지 않는지 알아봅니다.

🤔색인 요청이 성공한 직후, 장비 전원이 갑자기 꺼졌습니다. 그 문서는 어떻게 될까요?

참고자료

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