매핑 폭발(Mapping explosion)

매핑 폭발(Mapping explosion)

매핑 폭발은 인덱스가 과도한 수의 필드를 축적해 성능 저하, 메모리 문제, 클러스터 불안정을 초래하는 상황이에요. 문서 구조가 매우 변동적인 동적 매핑 환경에서 각 문서가 새 필드를 도입하며 흔히 발생해요.

출처: 문서

본문

매핑 폭발(mapping explosion)은 인덱스가 과도한 수의 필드를 축적하여 성능 저하, 메모리 문제, 클러스터 불안정을 초래할 때 발생합니다. 이 상황은 문서 구조가 매우 변동적이어서 각 새 문서가 인덱스 매핑에 자동으로 추가되는 추가 필드를 도입하는 동적 매핑(dynamic mapping)을 사용할 때 흔히 발생합니다.

OpenSearch는 문서에서 새 필드를 만나면 동적 매핑을 통해 자동으로 이 필드들의 매핑을 생성합니다. 이 기능은 유연성을 제공하지만 다음과 같은 시나리오에서 문제가 될 수 있습니다.

  • 구조가 다양한 로그 데이터: 서로 다른 로그 소스가 고유한 필드를 포함해 필드가 빠르게 늘어날 수 있습니다.
  • 사용자 생성 콘텐츠: 사용자가 맞춤형 필드나 속성을 정의할 수 있게 하는 애플리케이션.
  • 중첩 객체 구조: 많은 하위 필드를 포함하는 깊이 중첩된 객체가 있는 문서.
  • 시계열 데이터: 타임스탬프나 식별자에 기반한 동적 필드 이름을 포함하는 지표 또는 이벤트.

필드 수가 증가하면 여러 문제가 발생할 수 있습니다.

  • 필드 매핑 저장에 따른 메모리 소비 증가
  • 더 큰 매핑 구조로 인한 쿼리 성능 저하
  • 인덱싱 또는 검색 중 잠재적인 메모리 부족(out-of-memory) 오류
  • 클러스터 복구 시나리오의 어려움

매핑 제한 설정(Mapping limit settings)

OpenSearch는 매핑 증가의 여러 측면을 제한해 매핑 폭발을 방지하는 여러 인덱스 수준 설정을 제공합니다. 이 설정들은 인덱스 생성 시 구성하거나 기존 인덱스에 대해 갱신할 수 있습니다.

PUT /my-index/_settings
{
  "index.mapping.total_fields.limit": 2000
}

다음 표는 사용 가능한 모든 매핑 제한 설정을 나열합니다. 모든 설정은 동적(dynamic)입니다. 자세한 내용은 Dynamic settings를 참고하세요.

설정 기본값 유효 값 설명
index.mapping.total_fields.limit 1000 [0, ∞) 인덱스에 허용되는 최대 필드 수를 설정합니다. 일반 필드, 객체 매핑, 필드 별칭(alias)을 모두 포함합니다. 이 제한을 늘리려면 클러스터 리소스를 신중히 고려해야 합니다. 제한을 늘릴 때는 더 큰 쿼리를 수용하도록 indices.query.bool.max_clause_count 설정도 함께 조정하는 것을 고려하세요.
index.mapping.depth.limit 20 [1, 100] 필드 매핑의 최대 중첩 깊이를 제어합니다. 깊이는 루트 수준부터 시작해 중첩된 객체의 수준을 세어 계산합니다(루트 수준 필드는 깊이 1, 객체 중첩 1단계 안의 필드는 깊이 2 등).
index.mapping.nested_fields.limit 50 [0, ∞) 인덱스에 허용되는 고유한 nested 필드 유형의 수를 제한합니다. nested 필드는 특별한 처리와 추가 메모리가 필요하므로 이 설정은 과도한 리소스 소비를 방지하는 데 도움이 됩니다.
index.mapping.nested_objects.limit 10000 [0, ∞) 단일 문서가 모든 nested 필드 유형에 걸쳐 포함할 수 있는 중첩 JSON 객체의 총 수를 제한합니다. 이는 개별 문서가 인덱싱 중 과도한 메모리를 소비하는 것을 방지합니다.
index.mapping.field_name_length.limit 50000 [1, 50000] 필드 이름의 최대 허용 길이를 설정합니다. 이 설정은 지나치게 긴 필드 이름을 방지해 매핑 크기를 합리적으로 유지하는 데 도움이 될 수 있습니다.
index.mapper.dynamic true true,false 새 필드를 매핑에 동적으로 추가할지 여부를 결정합니다. false로 설정하면 통제되지 않는 필드 증가를 방지할 수 있습니다.

모범 사례(Best practices)

매핑 폭발을 방지하려면 다음 지침을 따르세요.

명시적 매핑 사용(Use explicit mappings)

동적 매핑에 의존하지 말고 가능하면 명시적 매핑을 정의하세요.

PUT /logs
{
  "mappings": {
    "properties": {
      "timestamp": {
        "type": "date"
      },
      "message": {
        "type": "text"
      },
      "level": {
        "type": "keyword"
      },
      "source": {
        "type": "keyword"
      }
    }
  }
}

동적 매핑 템플릿 구성(Configure dynamic mapping templates)

동적 템플릿을 사용해 새 필드가 어떻게 매핑될지 제어하세요.

PUT /logs
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

flat_object 필드 유형 사용(Use the flat_object field type)

임의의 키-값 쌍이 있는 문서에는 동적 매핑을 허용하는 대신 flat_object 필드 유형을 사용하세요.

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "attributes": {
        "type": "flat_object"
      }
    }
  }
}

동적 매핑 비활성화(Disable dynamic mapping)

스키마가 잘 정의된 인덱스에는 동적 매핑을 완전히 비활성화하세요.

PUT /structured-data
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "id": {
        "type": "keyword"
      },
      "value": {
        "type": "double"
      }
    }
  }
}

모니터링 및 유지보수(Monitoring and maintenance)

정기적인 모니터링은 매핑 증가를 조기에 감지하고 클러스터 성능에 영향을 주기 전에 조치를 취하는 데 도움이 됩니다. 다음과 같은 방법으로 필드 매핑을 모니터링할 수 있습니다.

현재 필드 개수 확인(Check the current field count)

인덱스의 필드 수를 모니터링하세요.

GET /my-index/_mapping

또한 Cluster Stats API를 사용해 필드 개수 정보를 얻을 수도 있습니다.

GET /_cluster/stats

문제가 있는 인덱스 식별(Identify problematic indexes)

인덱스 통계를 사용해 필드 수가 많은 인덱스를 찾으세요.

GET /_cat/indices?v&h=index,docs.count,store.size,pri.store.size&s=store.size:desc

사용하지 않는 필드 정리(Clean up unused fields)

동적 매핑이 활성화된 인덱스의 경우 더 제한적인 매핑으로 재인덱싱해 더 이상 필요하지 않은 필드를 정기적으로 검토하고 정리하세요.

매핑 폭발로부터의 복구(Recovery from mapping explosion)

인덱스가 이미 매핑 폭발을 겪었다면 다음을 수행하세요.

  • 실제로 필요한 필드를 파악합니다.
  • 명시적 매핑과 적절한 제한으로 새 인덱스를 생성합니다.
  • Reindex API를 사용해 불필요한 필드를 걸러내며 데이터를 재인덱싱합니다.
  • 별칭(alias)을 갱신해 새 인덱스를 가리키게 합니다.
  • 마이그레이션이 완료되면 이전 인덱스를 삭제합니다.
POST /_reindex
{
  "source": {
    "index": "old-index"
  },
  "dest": {
    "index": "new-index"
  },
  "script": {
    "source": "ctx._source.remove('unwanted_field')"
  }
}

더 알아보기 (Learn more)