본문

[OpenSearch 트러블슈팅] 대용량 단일 인덱스 병목 현상과 시계열 데이터 최적화

운영 중인 OpenSearch 클러스터에서 간헐적인 검색 지연쓰기 차단 현상이 발생했습니다.

모니터링 지표와 인덱스 구조를 분석하여 근본적인 원인을 도출하고 아키텍처 레벨의 해결책을 적용하는 과정을 정리합니다.


1. 클러스터 주요 증상 및 지표 분석

CloudWatch 대시보드와 클러스터 상태를 확인한 결과, 전형적인 리소스 병목 현상이 관찰되었습니다.

  • JVM 메모리 압력 폭주: Data Node의 JVM 압력이 20%~90% 사이를 오가며 급격한 변동폭을 보였습니다.
  • 빈번한 쓰기 차단(Writes Blocked): ClusterIndexWritesBlocked 이벤트가 다수 발생하여 붉은색 스파이크가 대시보드를 덮었습니다.
  • 저조한 Query Cache 적중률: 총 206만 건의 쿼리 중 적중률(Hit)은 21.7%에 불과하고, Miss 비율이 78.3%에 달했습니다.
  • 세그먼트 병합 지연: 전체 문서 대비 삭제 문서 비율이 11.2%(약 15만 건)로 누적되어 있었습니다.
  • 지연 시간 스파이크: 인덱싱 및 검색 지연 시간(Latency) 그래프에서 주기적인 튀어오름 현상이 확인되었습니다.


2. 근본 원인 파악

가장 큰 원인은 28.8GB에 달하는 거대한 단일 인덱스(xxx_xxx_info)에 있었습니다.

# 샤드 상태 확인
GET /_cat/shards?v&s=store:desc

index              shard prirep state     docs   store   ip        node
xxx_xxx_info       1     p      STARTED 100744   2.9gb   x.x.x.x   846647e...
xxx_xxx_info       3     p      STARTED 101023   2.8gb   x.x.x.x   5bd396f...
xxx_xxx_info       3     r      STARTED 101023   2.8gb   x.x.x.x   72bdd59...
...

 

🤔 샤드가 나뉘어 있는데도 왜 문제가 될까?
물리적으로 파티셔닝(샤딩)이 되어 있다 하더라도, 논리적으로 하나의 거대한 단일 인덱스로 묶여 있으면 다음과 같은 악순환이 발생합니다.

1. 동시다발적인 캐시 무효화(Invalidation): 샤드 중 어느 한 곳에라도 새로운 쓰기가 발생하면 전체 인덱스의 쿼리 캐시가 무효화되어 효율이 급감합니다.
2. 과도한 GC 및 JVM 부하: 과거와 최신 데이터가 무작위로 섞여 있어, 데이터 업데이트 시 모든 샤드에서 끊임없이 세그먼트 병합(Segment Merge)이 발생합니다.
3. 비효율적인 검색 (Scatter and Gather): 최신 데이터만 검색하려 해도 5개 샤드 전체를 항상 뒤져야 하는 비효율이 발생합니다.

3. 핵심 해결 방안: ISM Rollover 적용

하드웨어 스케일업은 임시방편일 뿐입니다. 아키텍처 개선을 위해 시계열 인덱스 구조로 전환했습니다.

  • ISM(Index State Management) 도입: 단일 인덱스를 용량별(예: 5GB) 또는 기간별로 자동 롤오버되도록 설정합니다.
  • 쓰기와 읽기 부하 격리: 쓰기는 최신(Hot) 인덱스에만 집중시키고, 과거 인덱스는 Read-Only 상태로 전환하여 캐시 효율을 극대화합니다.
  • 안전한 Force Merge: 쓰기가 멈춘 과거 인덱스에만 비피크 시간대에 Force Merge를 실행하여 삭제 문서를 정리하고 세그먼트를 압축합니다.

4. OpenSearch ISM 정책 구성 예시

1) ISM Policy 정의

PUT _plugins/_ism/policies/xxx_time_series_policy
{
  "policy": {
    "description": "데이터(xxx_xxx_info)를 위한 시계열 롤오버 및 비용 최적화 정책",
    "default_state": "hot",
    "states": [
      {
        "name": "hot",
        "actions": [
          {
            "rollover": {
              "min_size": "5gb",
              "min_index_age": "30d"
            }
          }
        ],
        "transitions": [{ "state_name": "warm" }]
      },
      {
        "name": "warm",
        "actions": [
          { "read_only": {} },
          { "force_merge": { "max_num_segments": 1 } }
        ],
        "transitions": [
          {
            "state_name": "delete",
            "conditions": { "min_index_age": "365d" }
          }
        ]
      },
      {
        "name": "delete",
        "actions": [{ "delete": {} }],
        "transitions": []
      }
    ],
    "ism_template": [
      {
        "index_patterns": ["xxx_xxx_info-*"],
        "priority": 100
      }
    ]
  }
}

 

2) 논리적 단일 진입점 유지 (Alias)

애플리케이션 수정 없이 그대로 xxx_xxx_info라는 이름을 사용할 수 있도록 Alias를 설정합니다.

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "xxx_xxx_info-000001",
        "alias": "xxx_xxx_info",
        "is_write_index": true
      }
    }
  ]
}

5. 마무리 및 인사이트

 
비교 항목 Alias + ISM Rollover Data Stream
동작 방식 수동으로 Alias(별칭)를 생성하고 ISM으로 롤오버 제어 OpenSearch가 백그라운드 인덱스(Backing Indices)를 완전 자동 관리
수정/삭제 자유롭게 가능 (O) 원칙적으로 불가 (Append-Only)
초기 세팅 첫 인덱스 및 별칭(Alias) 부트스트랩 수동 진행 필요 템플릿 설정만으로 완전 자동화 (매우 간편)
필수 필드 제한 없음 @timestamp 필드 강제
적합한 케이스 내용 업데이트가 빈번한 정보성 데이터 서버 로그, 시스템 메트릭

 

결론: 데이터가 한 번 쌓인 후 절대 변하지 않는다면 Data Stream이 유리하지만, 본 사례의 xxx_xxx_info 데이터처럼 사후 수정이나 삭제가 발생할 가능성이 있다면 Alias + ISM Rollover 방식이 훨씬 유연하고 안전한 선택입니다.

공유

댓글

Cloud & AI Engineering | 임승한

design by tokiidesu. powerd by AXZ.