본문 바로가기
OpenSearch

OpenSearch 데이터 인덱싱 과정과 translog

by 코엘리 2025. 9. 7.
반응형

이번에 새로 맡게 된 프로젝트를 진행하면서 로그 저장소로 OpenSearch를 사용하게 되었다. 이를 계기로 OpenSearch의 인덱싱 구조와 동작 방식을 공부했고, 그 과정에서 정리한 내용을 공유하고자 한다.

인덱싱 성능 이슈에서 시작된 이야기

사실 미리 공부했어야 했는데, 계기는 조금 급했다. 미팅에서 OpenSearch 성능 최적화를 위해 "translog fsync 주기를 조정해야 한다"는 이야기가 나왔을 때, 솔직히 무슨 말인지 잘 따라가지 못했다. 내가 맡은 프로젝트 운영에 대한 이야기였는데, 못알아 듣는게 조금 창피하기도 했고 해서 제대로 정리해보자는 마음으로 공부를 시작했다.

참고로 OpenSearch는 Elasticsearch와 동일하게 Apache Lucene 기반으로 동작한다. 따라서 기본 개념은 Elasticsearch 관련 자료를 중심으로 정리했다. 

Index 구조: Document → Index → Shard → Segment

  • Document: 인덱싱 단위, JSON 형식의 문서
  • Index: 여러 Document의 논리적 모음
  • Shard: Index를 물리적으로 저장하는 Lucene 인스턴스 (Primary/Replica 구성 가능)
  • Segment: Shard 내부의 불변 데이터 파일 단위

Lucene Index vs. ElasticSearch Index

  • Lucene Index = Segment 1 + Segment 2 + ... + Segment n
  • Elastic Search Shard = Lucene Index 하나를 래핑한 단위
  • Elastic Search Index = Shard 1 + Shard 2 + ... + Shard n

인덱싱 메커니즘: Lucene 중심으로 보기

  1. 문서 색인 요청
    • 루씬은 문서를 분석 후 역색인(Inverted Index)을 생성한다.
    • 이때 디스크에 바로 저장되는 것이 아니라 메모리 버퍼에 먼저 적재된다.
  2. Lucene Flush
    • 메모리에 있는 변경 작업을 작은 세그먼트 단위로 디스크 캐시에 flush 한다.
    • 이는 시스템 페이지 캐시에 데이터를 넘기는 동작을 의미한다.
    • Elastic Search에서는 이 과정을 Refresh라고 부르며, 기본 주기는 1초(index.refresh_interval)다.
    • Refresh가 있어야 문서를 검색할 수 있다.
  3. Lucene Commit (fsync)
    • 루씬은 이후 fsync 시스템 콜을 통해 주기적으로 커널 시스템 페이지 캐시 내용디스크에 기록된 내용 동기화한다.
    • Elastic SearchFlush 작업은 내부적으로 이 Lucene Commit을 포함한다.
  4. Segment 생성 & 병합
    • 세그먼트는 루씬의 검색 단위이며, 문서는 반드시 세그먼트에 담겨야 검색 가능하다.
    • 세그먼트는 불변 구조이므로 새 문서가 들어오면 새로운 세그먼트가 생성된다. 삭제/업데이트 시에도 삭제 플래그만 표시 후 새로운 세그먼트를 만든다.
    • 세그먼트가 무한정 늘어날 수 없기 때문에, 주기적으로 병합(Merge) 작업을 수행하여 삭제 문서를 실제로 제거하고 검색 성능을 최적화한다.

Translog: 왜 필요한가?

문서가 Lucene Commit까지 완료되어야만 디스크에 안전하게 기록된다. 하지만 매번 Commit을 하면 I/O 비용이 너무 크고, 반대로 모아서 Commit하면 데이터 유실 위험이 있다.

이 문제를 해결하는 것이 Translog다.

    • 모든 인덱싱/삭제 작업은 메모리 버퍼 기록 직후 Translog에 먼저 기록된다.
    • 장애가 발생하면 마지막 Commit 이후 작업은 Translog를 통해 복구할 수 있다.
    • 전통적인 DBMS의 WAL(Write Ahead Log)와 유사하지만, 순서가 다르다. (Lucene은 메모리 → Translog 순으로 기록한다.)

ElasticSearch Flush: Lucene Commit + New Translog

Elasticsearch의 Flush는 Lucene Commit을 수행하고 새로운 Translog를 생성하는 과정이다.

  • Flush가 발생하면 기존 Translog는 비워지고, 디스크에 fsync되지 않은 데이터만 새 Translog에 보관된다.
  • 즉, Translog는 항상 아직 Commit되지 않은 변경 내역을 담는 역할을 한다.

Translog 데이터는 Elastic Search의 Flush와 별개로, translog의 fsync가 일어나야만 디스크에 영구 저장된다.

Translog fsync 전략: request vs async

성능과 안정성의 균형을 결정짓는 중요한 설정이 바로 index.translog.durability다.

  • index.translog.durability=request (기본값)
    • 모든 요청마다 fsync 수행
    • 안정성은 높지만 성능은 떨어진다.
  • index.translog.durability=async
    • 일정 주기(index.translog.sync_interval, 기본 5초)마다 fsync 수행
    • 성능은 높지만, 유실 가능성이 존재한다.

로그 서비스처럼 인덱싱 비율이 압도적으로 높은 경우에는 async 모드가 성능 최적화에 유리하다. 반면 금융/거래 데이터처럼 단 1건의 손실도 치명적인 경우라면 request 모드를 유지해야 한다.

전체 인덱싱 과정 요약

  1. 문서 인덱싱 요청 → Lucene 인메모리 버퍼 적재 + Translog 기록
  2. 매 1초 → Refresh (디스크 캐시에 작은 세그먼트 기록) → 검색 가능
  3. 주기적 Flush → Commit + 새로운 Translog → 안정성 확보
  4. 백그라운드 → Segment Merge → 검색 성능 최적화

참고 자료

 

반응형