백분위 집계
백분위 집계 (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]
}
}
}
}