백분위 집계

백분위 집계 (Percentile aggregation)

percentiles 집계는 숫자 필드의 주어진 백분위에 해당하는 값을 추정해요. 이는 분포의 경계를 이해하는 데 유용해요.

예를 들어, load_time의 95번째 백분위가 120ms라면 값의 95%가 120ms보다 작거나 같다는 뜻이에요. cardinality 메트릭과 마찬가지로 백분위 메트릭도 근사치예요.

출처: 문서

본문

파라미터

percentiles 집계는 다음과 같은 파라미터를 받아요.

파라미터 데이터 타입 필수/선택 설명
field String 필수 백분위를 계산하는 데 사용할 숫자 필드예요.
percents Double 배열 선택 응답에 반환할 백분위 목록이에요. 기본값은 [1, 5, 25, 50, 75, 95, 99]예요.
keyed Boolean 선택 false로 설정하면 결과를 배열로 반환해요. 그 외에는 결과를 JSON 객체로 반환해요. 기본값은 true예요.
tdigest.compression Double 선택 tdigest 알고리즘의 정확도와 메모리 사용량을 제어해요. tdigest를 이용한 정밀도 조정 섹션을 참고하세요.
hdr.number_of_significant_value_digits Integer 선택 HDR 히스토그램의 정밀도 설정이에요. HDR 히스토그램 섹션을 참고하세요.
missing Number 선택 문서에 대상 필드가 없을 때 사용할 기본값이에요.
script Object 선택 필드 대신 사용자 지정 값을 계산하는 데 사용할 스크립트예요. 인라인(inline) 및 저장된(stored) 스크립트를 지원해요.

예제

먼저 인덱스를 생성해 봐요:

PUT /latency_data
{
  "mappings": {
    "properties": {
      "load_time": {
        "type": "double"
      }
    }
  }
}

백분위 계산을 설명하기 위해 샘플 숫자 값을 추가해요:

POST /latency_data/_bulk
{ "index": {} }
{ "load_time": 20 }
{ "index": {} }
{ "load_time": 40 }
{ "index": {} }
{ "load_time": 60 }
{ "index": {} }
{ "load_time": 80 }
{ "index": {} }
{ "load_time": 100 }
{ "index": {} }
{ "load_time": 120 }
{ "index": {} }
{ "load_time": 140 }

백분위 집계

다음 예제는 load_time 필드에 대해 기본 백분위 세트를 계산해요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time"
      }
    }
  }
}

기본적으로 1번째, 5번째, 25번째, 50번째, 75번째, 95번째, 99번째 백분위가 반환돼요:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": {
        "1.0": 20,
        "5.0": 20,
        "25.0": 40,
        "50.0": 80,
        "75.0": 120,
        "95.0": 140,
        "99.0": 140
      }
    }
  }
}

사용자 지정 백분위 (Custom percentiles)

percents 배열을 사용해 정확한 백분위를 지정할 수 있어요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "percents": [50, 90, 99]
      }
    }
  }
}

응답에는 요청한 세 가지 백분위 집계만 포함돼요:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": {
        "50.0": 80,
        "90.0": 140,
        "99.0": 140
      }
    }
  }
}

키 기반 응답 (Keyed response)

keyed 파라미터를 false로 설정하면 반환되는 집계 형식을 JSON 객체에서 키-값 쌍 목록으로 바꿀 수 있어요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "keyed": false
      }
    }
  }
}

응답은 백분위를 값 배열로 제공해요:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": [
        {
          "key": 1,
          "value": 20
        },
        {
          "key": 5,
          "value": 20
        },
        {
          "key": 25,
          "value": 40
        },
        {
          "key": 50,
          "value": 80
        },
        {
          "key": 75,
          "value": 120
        },
        {
          "key": 95,
          "value": 140
        },
        {
          "key": 99,
          "value": 140
        }
      ]
    }
  }
}

tdigest를 이용한 정밀도 조정 (Precision tuning with tdigest)

tdigest 알고리즘은 백분위를 계산하는 기본 방법이에요. 응답 시간이나 지연 시간 같은 부동소수점 데이터를 다룰 때 백분위 순위를 메모리 효율적으로 추정할 수 있게 해줘요.

정확한 백분위 계산과 달리 tdigest는 값을 센트로이드(centroid)—분포를 요약하는 작은 클러스터—로 그룹화하는 확률적 접근 방식을 사용해요. 이 방식은 모든 원시 데이터를 메모리에 저장하지 않고도 대부분의 백분위에 대한 정확한 추정을 가능하게 해요.

이 알고리즘은 분포의 꼬리 근처—낮은 백분위(예: 1번째)와 높은 백분위(예: 99번째)—에서 매우 정확하도록 설계됐어요. 이 꼬리 값들은 성능 분석에서 가장 중요한 경우가 많아요. compression 파라미터를 사용해 결과의 정밀도를 제어할 수 있어요.

compression 값이 높을수록 더 많은 센트로이드가 사용되며, 이는 정확도를 높이지만(특히 꼬리에서) 더 많은 메모리와 CPU를 요구해요. compression 값이 낮으면 메모리 사용량이 줄고 실행 속도가 빨라지지만 결과가 덜 정확할 수 있어요.

다음과 같은 경우 tdigest를 사용하세요:

  • 응답 시간, 지연 시간 또는 기간 같은 부동소수점 값이 데이터에 포함된 경우
  • 극단적인 백분위(예: 1번째 또는 99번째)에서 정확한 결과가 필요한 경우

다음과 같은 경우 tdigest를 피하세요:

  • 정수 데이터만 다루고 최대 속도를 원하는 경우
  • 분포 꼬리의 정확도에 덜 신경 쓰고 더 빠른 집계를 선호하는 경우(hdr 사용을 고려하세요)

다음 예제는 tdigest.compression을 200으로 설정해요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "tdigest": {
          "compression": 200
        }
      }
    }
  }
}

HDR 히스토그램

HDR(High Dynamic Range) 히스토그램은 백분위 계산을 위한 tdigest의 대안이에요. 대규모 데이터셋과 지연 시간 측정을 다룰 때 특히 유용해요. 속도를 위해 설계되었으며, 고정되고 구성 가능한 수준의 정밀도를 유지하면서 넓은 동적 범위의 값을 지원해요.

분포의 꼬리(극단적인 백분위)에서 더 높은 정확도를 제공하는 tdigest와 달리, HDR은 속도와 범위 전반에 걸친 균일한 정확도를 우선시해요. 버킷 개수가 많고 희귀한 값의 극단적인 정밀도가 필요하지 않을 때 가장 잘 작동해요.

예를 들어, 1마이크로초에서 1시간까지의 응답 시간을 측정하고 HDR을 유효 숫자 3자리로 구성했다면, 1밀리초까지의 값은 ±1마이크로초, 1시간 근처의 값은 ±3.6초의 정밀도로 기록돼요.

이 트레이드오프 덕분에 HDR은 tdigest보다 훨씬 빠르고 메모리를 더 많이 사용해요.

다음 표는 HDR 유효 숫자의 세부 사항을 보여줘요.

유효 숫자 상대 정밀도(최대 오차)
1 10분의 1 = 10%
2 100분의 1 = 1%
3 1,000분의 1 = 0.1%
4 10,000분의 1 = 0.01%
5 100,000분의 1 = 0.001%

다음과 같은 경우 HDR을 사용해야 해요:

  • 많은 버킷에 걸쳐 집계하는 경우
  • 꼬리 백분위수에서 극단적인 정밀도를 요구하지 않는 경우
  • 충분한 메모리를 사용할 수 있는 경우

다음과 같은 경우 HDR을 피해야 해요:

  • 꼬리 정확도가 중요한 경우
  • 왜곡되거나 희소한 데이터 분포를 분석하는 경우

다음 예제는 hdr.number_of_significant_value_digits를 3으로 설정했어요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "hdr": {
          "number_of_significant_value_digits": 3
        }
      }
    }
  }
}

누락 값 처리 (Missing values)

대상 필드가 없는 문서에 대한 대체(fallback) 값을 구성하려면 missing 설정을 사용해요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "missing": 0
      }
    }
  }
}

스크립트 (Script)

필드를 지정하는 대신 스크립트를 사용해 값을 동적으로 계산할 수 있어요. 통화 변환 또는 가중치 적용 같은 변환을 적용해야 할 때 유용해요.

인라인 스크립트 (Inline script)

스크립트를 사용해 파생 값을 계산해요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "adjusted_percentiles": {
      "percentiles": {
        "script": {
          "source": "doc['load_time'].value * 1.2"
        },
        "percents": [50, 95]
      }
    }
  }
}

저장된 스크립트 (Stored script)

먼저 다음 요청으로 샘플 스크립트를 생성해요:

POST _scripts/load_script
{
  "script": {
    "lang": "painless",
    "source": "doc[params.field].value * params.multiplier"
  }
}

그런 다음 percentiles 집계에서 저장된 스크립트를 사용하고, 저장된 스크립트가 필요로 하는 params를 제공해요:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "adjusted_percentiles": {
      "percentiles": {
        "script": {
          "id": "load_script",
          "params": {
            "field": "load_time",
            "multiplier": 1.2
          }
        },
        "percents": [50, 95]
      }
    }
  }
}

더 알아보기 (Learn more)