Elastic Search

Ch05. 데이터 집계 - 버킷 집계

webmaster 2025. 9. 18. 22:18
728x90

버킷 집계는 메트릭 집계와는 다르게 메트릭을 계산하지 않고 버킷을 생성한다. 생성된 버킷은 쿼리와 함께 수행되어 쿼리 결과에 따른 컨텍스트 내에서 집계가 이뤄진다. 집계된 버킷은 또 다시 하위에서 집계를 한번 더 수행해서 집계된 결과에 대해 중첩된 집계를 수행하는 것이 가능하다.

버킷을 생성하는건 집계된 데이터 집합을 메모리에 저장한다는 의미로, 중첩되는 단계가 깊어질수록 메모리 사용량은 점점 더 증가해 성능에 악영향을 줄 수 있다. -> 엘라스틱서치에서는 기본적으로 사용 가능한 최대 버킷 수가 정의되어 있으며, search.max_buckets 값을 변경하는 것으로 조정할 수 있다.

질의 요청 시, 버킷 크기를 -1(전체) 또는 10000 이상의 값으로 지정하면 엘라스틱서치에서는 경고 메시지를 반환한다. 안정적인 집계를 위해서는 성능 측면을 충분히 고려한 후, 집계를 수행해야 한다.

범위 집계

Request

GET /apache-web-log/_search?size=0
{
  "aggs": {
    "bytes_range": {
      "range": {
        "field": "bytes",
        "ranges": [
          { "from": 1000, "to": 2000 },
          //{ "key": "small", "from": 1000, "to": 2000 } // key 지정 가능
          { "from": 2000, "to": 5000 },
          { "from": 5000 }   // to 생략하면 "5000 이상" 의미
        ]
      }
    }
  }
}

Response

"aggregations": {
  "bytes_range": {
    "buckets": [
      { "key": "1000.0-2000.0", "from": 1000, "to": 2000, "doc_count": 123 },
      //{ "key": "small", "from": 1000, "to": 2000, "doc_count": 123 }, //키 지정시 노출
      { "key": "2000.0-5000.0", "from": 2000, "to": 5000, "doc_count": 456 },
      { "key": "5000.0-*", "from": 5000, "doc_count": 78 }
    ]
  }
}
  • 사용자가 지정한 범위 내에서 집계를 수행하는 다중 버킷 집계이다.
  • 집계를 수행하면 추출된 문서가 범위에 해당하는지 검증하게 되고, 범위에 해당하는 문서들에 대해서만 집계가 수행된다.
  • 범위 집계에서는 from과 to 속성을 지정하는데, from을 시작으로 to까지 범위 내에서만 집계가 수행된다.
    • to에 지정한 값은 결과에서 제외되므로 주의
  • Response 응답
    • key: 집계가 수행될 범위 -> key는 기본적으로 범위가 지정되어 있으나, 직접 원하는 정보를 설정할 수도 있음
    • from: 범위의 시작 값
    • to: 범위의 끝 값(해당 값 제외)
    • doc_count: 범위 내의 문서 수

날짜 범위 집계

Request

GET /apache-web-log/_search?size=0
{
  "aggs": {
    "request_count_with_date_range": {
      "date_range": {
        "field": "timestamp",
        "ranges": [
          {
            "from": "2015-01-04T05:14:00.000Z",
            "to": "2015-01-04T05:16:00.000Z"
          },
          {
            "from": "2015-01-04T05:16:00.000Z",
            "to": "2015-01-04T05:18:00.000Z"
          }
        ]
      }
    }
  }
}

Response

"aggregations": {
  "request_count_with_date_range": {
    "buckets": [
      {
        "key": "2015-01-04T05:14:00.000Z-2015-01-04T05:16:00.000Z",
        "from": 1420348440000,
        "from_as_string": "2015-01-04T05:14:00.000Z",
        "to": 1420348560000,
        "to_as_string": "2015-01-04T05:16:00.000Z",
        "doc_count": 120
      },
      {
        "key": "2015-01-04T05:16:00.000Z-2015-01-04T05:18:00.000Z",
        "from": 1420348560000,
        "from_as_string": "2015-01-04T05:16:00.000Z",
        "to": 1420348680000,
        "to_as_string": "2015-01-04T05:18:00.000Z",
        "doc_count": 98
      }
    ]
  }
}
  • 날짜 값을 범위로 집계를 수행한다.(from 속성에는 시작 날짜 값을 설정하고, to 속성에는 범위의 마지막 날짜 값을 설정한다)
    • 마지막 날짜는 제외
  • Response 응답 값
    • key: 집계에 대한 날짜 범위
    • from: 시작 날짜에 해당하는 밀리초 값
    • from_as_string: 시작 날짜의 문자열 표현
    • to: 종료 날짜에 해당하는 밀리초 값
    • to_as_string: 종료 날짜의 문자열 표현
    • doc_count: 날짜 범위에 해당하는 문서 수

히스토리그램 집계

Request

GET /apache-web-log/_search?size=0
{
  "aggs": {
    "bytes_histogram": {
      "histogram": {
        "field": "bytes",
        "interval": 10000,
        //"min_doc_count": 1
      }
    }
  }
}

Response

"aggregations": {
  "bytes_histogram": {
    "buckets": [
      {
        "key": 0,
        "doc_count": 120
      },
      {
        "key": 10000,
        "doc_count": 98
      },
      {
        "key": 20000,
        "doc_count": 0
      },
      {
        "key": 30000,
        "doc_count": 45
      }
    ]
  }
}
  • 숫자 범위를 처리하기 위한 집계로, 지정한 수치가 간격을 나타내고, 이 간격의 범위 내에서 집계를 수행한다.
  • 문서가 존재하지 않는 구간은 필요하지 않다면 최소 문서 수(min_doc_count)를 설정해서 해당 구간은 제외할 수 있다.

날짜 히스토리그램 집계

Request

GET /apache-web-log/_search?size=0
{
  "aggs": {
    "daily_request_count": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "minute",
        //"format": "yyyy-MM-dd", //응답 format
        //"time_zone": "+09:00", //Time Zone(서울)
        //"offset": "+3h", //3시간 후부터 집계가 시작되도록
      }
    }
  }
}

Response

"aggregations": {
  "daily_request_count": {
    "buckets": [
      {
        "key_as_string": "2015-01-04T05:14:00.000Z",
        "key": 1420358040000,
        "doc_count": 12
      },
      {
        "key_as_string": "2015-01-04T05:15:00.000Z",
        "key": 1420358100000,
        "doc_count": 0
      },
      {
        "key_as_string": "2015-01-04T05:16:00.000Z",
        "key": 1420358160000,
        "doc_count": 5
      },
      {
        "key_as_string": "2015-01-04T05:17:00.000Z",
        "key": 1420358220000,
        "doc_count": 0
      },
      {
        "key_as_string": "2015-01-04T05:18:00.000Z",
        "key": 1420358280000,
        "doc_count": 9
      },
      {
        "key_as_string": "2015-01-04T05:19:00.000Z",
        "key": 1420358340000,
        "doc_count": 0
      },
      {
        "key_as_string": "2015-01-04T05:20:00.000Z",
        "key": 1420358400000,
        "doc_count": 3
      }
    ]
  }
}
  • Response 응답
    • key_as_string: 지정한 interval 값에 따른 구간 시작 일자(UTC, yyyy-MM-dd HH:mm:ss:SSS 형식)
    • key: 1의 날짜에 해당하는 밀리초 값
    • doc_count: 해당 구간 문서 수  
  • 분, 시간, 월, 연도를 구간으로 집계를 수행할 수 있다. 
  • 구간을 지정하기 위해서는 interval 속성을 사용하면 되며, year, quarter, month, week, day, hour, minute, second 표현식을 사용할 수 있다.
    • 더 세밀한 설정은 30m(30분), 1.5h(1시간 반) 같은 값으로 설정할 수 있다.
  • format 속성을 통해 반환되는 날짜 형식을 변경할 수 있다.
  • 엘라스틱서치는 기본적으로 UTC 기준으로 시간을 제공하며, timeZone 속성을 통해 한국 시간으로 변환된 값을 받을 수 있다.
  • offset을 사용하면, 집계 기준이 되는 날짜 값의 시작일자를 조정할 수 있다.

텀즈 집계

Request

GET /apache-web-log/_search
{
  "size": 0,
  "aggs": {
    "request_count_by_country": {
      "terms": {
        "field": "geoip.country_name.keyword",
        "size": 10
      }
    }
  }
}

Response

{
  "aggregations": {
    "request_count_by_country": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        { "key": "United States", "doc_count": 3974 },
        { "key": "France", "doc_count": 855 },
        { "key": "Germany", "doc_count": 510 },
        { "key": "Sweden", "doc_count": 440 },
        { "key": "India", "doc_count": 428 },
        { "key": "China", "doc_count": 416 }
      ]
    }
  }
}
  • Response 응답 정리
    • doc_count_error_upper_bound: 문서 수에 대한 오류의 상한 선(각 샤드별 계산되는 집계의 성능을 고려해 근사치를 계산하기 때문에 문서 수가 정확하지 않을 수 있다.)
    • sum_other_doc_count: 결과에 포함되지 않은 모든 문서 수
    • buckets: 최상위 버킷 목록(집계된 결과에서 상위 결과로부터 설정한 size만큼을 반환, 기본 10)
    • key: 질의 시 지정한 필드에 해당하는 값
    • doc_count: 해당 필드 값과 동일한 문서의 수
  • 집계 시 지정한 필드에 대해 빈도수가 높은 텀의 순위로 결과가 반환된다.
    • 가장 많이 접속하는 사용자나, 국가별 접근 빈도 등의 집계를 수행가능하다.
  • Terms 집계의 필드 값으로 Keyword 데이터 타입을 명시해야 하며, Text 타입 같은 경우 형태소 분석기를 통해 분석하는 과정이 항상 동반되기 때문에 주의가 필요하다.
  • sum_other_doc_count는 반환된 결과에 포함되지 않은 집계 결과가 남아있다는 것을 의미하기 때문에 size 속성의 기본 값이 10개보다 많은 결과를 반환받기 위해서는 size 값 지정이 필요하다.
  • 집계를 수행할 때는 각 샤드에 집계 요청을 전달하고, 각 샤드가 집계 결과에 대해 정렬을 수행한 자체 뷰를 갖게 되며 이를 병합해 최종 뷰를 만들기 때문에 포함되지 않은 문서가 존재할 경우 집계 결과가 정확하지 않을 수 있다.
    • 집계 시, 모든 문서가 포함되지 않은 경우 정확하지 않은 결과가 반환될 수 있음에 주의
    • size 값을 늘리면 집계의 정확도가 올라가지만 더 많은 양의 데이터를 담아야 하기 때문에 메모리 사용량과 결과를 계산하는데 드는 처리비용이 늘어난다.
  • doc_count_error_upper_bound는 최종 집계 결과에 포함되지 않은 잠재 문서의 수를 의미하며, 이 수치는 각 샤드별 반환된 집계 결과 중 최종 병합 과정에서 선택받지 못한 집계 결과의 가장 마지막 값을 합산한 결과다.
    • 현재 집계된 결과보다 해당 값이 높게 나오면 size와 shard_size를 조절해서 정확도를 높이는 것이 좋다
참고
집계와 샤드 크기: 텀즈 집계가 수행될 때 검색 프로세스를 관장하는 노드에서는 각 샤드에게 최상위 버킷을 제공하도록 요청 후 모든 샤드로부터 결과를 받을 때까지 대기하는데, 모든 샤드로부터 결과를 받으면 설정된 size에 맞춰 하나로 병합한 후 Client에게 결과로 전달한다.
각 샤드는 정확성을 위해 size 크기가 아닌 샤드 크기(size * 1.5 + 10)를 사용해 내부적으로 집계를 수행하며, 텀즈 집계의 결과로 받을 텀의 개수를 정확하게 판단할 수 있는 경우에는 shard_size 옵션을 통해 집계할 크기를 직접 지정해 불필요한 연산을 없애고, 정확도를 높일 수 있다.
샤드 크기가 -1로 설정될 경우 엘라스틱서치는 샤드 크기를 자동으로 추정하며, 직접 설정할 경우 size 보다 작은 값으로 설정할 수 없다.

공식 문서: https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-terms-aggregation#search-aggregations-bucket-terms-aggregation-shard-size
728x90