TopN 쿼리

TopN 쿼리

Druid native TopN 쿼리를 소개해요. 단일 dimension의 값들을 기준에 따라 정렬된 결과 집합으로 반환하는 근사 쿼리로, GroupBy보다 훨씬 빠르고 리소스 효율적이에요. 쿼리 구조와 multi-value dimension 동작, 근사(approximation) 특성을 다뤄요.

출처: 문서

본문

Apache Druid는 Druid SQL과 native 쿼리 두 가지 쿼리 언어를 지원해요. 이 문서는 native 언어의 쿼리 유형을 설명해요. Druid SQL이 이 쿼리 유형을 언제 사용하는지에 대해서는 SQL 문서를 참고해요.

Apache Druid TopN 쿼리는 주어진 dimension의 값들에 대해 어떤 기준에 따라 정렬된 결과 집합을 반환해요. 개념적으로 Ordering spec을 가진 단일 dimension에 대한 근사 GroupByQuery라고 생각할 수 있어요. 이 사용 사례에서 TopN은 GroupBy보다 훨씬 빠르고 리소스 효율적이에요. 이런 유형의 쿼리는 topN 쿼리 객체를 받아서 JSON 객체 배열을 반환하며, 각 객체는 topN 쿼리가 요청한 값을 나타내요.

TopN은 각 데이터 프로세스가 자신의 상위 K 결과를 순위 매기고 상위 K 결과만 Broker에 반환한다는 점에서 근사적이에요. Druid의 기본 K 는 max(1000, threshold) 예요.

topN 쿼리 객체는 다음과 같아요:

{  "queryType": "topN",  "dataSource": "sample_data",  "dimension": "sample_dim",  "threshold": 5,  "metric": "count",  "granularity": "all",  "filter": {    "type": "and",    "fields": [      {        "type": "selector",        "dimension": "dim1",        "value": "some_value"      },      {        "type": "selector",        "dimension": "dim2",        "value": "some_other_val"      }    ]  },  "aggregations": [    {      "type": "longSum",      "name": "count",      "fieldName": "count"    },    {      "type": "doubleSum",      "name": "some_metric",      "fieldName": "some_metric"    }  ],  "postAggregations": [    {      "type": "arithmetic",      "name": "average",      "fn": "/",      "fields": [        {          "type": "fieldAccess",          "name": "some_metric",          "fieldName": "some_metric"        },        {          "type": "fieldAccess",          "name": "count",          "fieldName": "count"        }      ]    }  ],  "intervals": [    "2013-08-31T00:00:00.000/2013-09-03T00:00:00.000"  ]}

topN 쿼리에는 11개의 부분이 있어요.

| property | description | required? | | queryType | 이 String은 항상 "topN"이어야 해요. Druid가 쿼리를 어떻게 해석할지 결정하기 위해 가장 먼저 보는 값이에요 | yes | | dataSource | 쿼리할 데이터 소스를 정의하는 String 또는 Object예요. 관계형 데이터베이스의 테이블과 매우 유사해요. 자세한 내용은 DataSource 문서 참고 | yes | | intervals | ISO-8601 Intervals를 나타내는 JSON Object예요. 쿼리를 실행할 시간 범위를 정의해요. | yes | | granularity | 쿼리 결과를 버킷화할 세분성을 정의해요. Granularities 문서 참고 | yes | | filter | Filters 문서 참고 | no | | virtualColumns | virtual column 의 JSON 목록. 가상 컬럼을 그룹핑 dimension 또는 aggregations/postAggregations 의 입력으로 참조할 수 있어요. | no (기본 none) | | aggregations | Aggregations 문서 참고 | 숫자 metricSpec의 경우 aggregations 또는 postAggregations 를 지정해야 해요. 그 외에는 no | | postAggregations | Post Aggregations 문서 참고 | 숫자 metricSpec의 경우 aggregations 또는 postAggregations 를 지정해야 해요. 그 외에는 no | | dimension | top을 구할 dimension을 정의하는 String 또는 JSON object. 자세한 내용은 DimensionSpecs 문서 참고 | yes | | threshold | topN의 N (즉, top 목록에서 원하는 결과 수)을 정의하는 정수예요 | yes | | metric | top 목록을 정렬할 metric을 지정하는 String 또는 JSON object. 자세한 내용은 TopNMetricSpec 문서 참고 | yes | | context | Query context reference 문서 참고 | no |

context JSON 객체는 topN 쿼리에서도 사용 가능하며, timeseries 경우와 같은 주의를 기울여 사용해야 해요. 결과 형식은 다음과 같아요:

[  {    "timestamp": "2013-08-31T00:00:00.000Z",    "result": [      {        "dim1": "dim1_val",        "count": 111,        "some_metrics": 10669,        "average": 96.11711711711712      },      {        "dim1": "another_dim1_val",        "count": 88,        "some_metrics": 28344,        "average": 322.09090909090907      },      {        "dim1": "dim1_val3",        "count": 70,        "some_metrics": 871,        "average": 12.442857142857143      },      {        "dim1": "dim1_val4",        "count": 62,        "some_metrics": 815,        "average": 13.14516129032258      },      {        "dim1": "dim1_val5",        "count": 60,        "some_metrics": 2787,        "average": 46.45      }    ]  }]

Multi-value dimension에서의 동작

topN 쿼리는 multi-value dimension에 대해 그룹핑할 수 있어요. multi-value dimension에 대해 그룹핑할 때, 일치하는 행의 모든 값이 값별로 그룹 하나를 생성하는 데 사용돼요. 쿼리가 행 수보다 더 많은 그룹을 반환할 수 있어요. 예를 들어 필터 "t1" AND "t3" 가 있는 tags dimension에 대한 topN은 row1만 일치시키고, t1 , t2 , t3 세 그룹이 있는 결과를 생성해요. 필터와 일치하는 값만 포함해야 한다면 filtered dimensionSpec 을 사용할 수 있어요. 이는 성능도 향상시킬 수 있어요.

자세한 내용은 Multi-value dimensions 문서를 참고해요.

근사(Aliasing)

현재 TopN 알고리즘은 근사 알고리즘이에요. 각 segment의 로컬 상위 1000 결과가 글로벌 topN을 결정하기 위해 병합을 위해 반환돼요. 따라서 topN 알고리즘은 순위와 결과 모두에서 근사적이에요. 근사 결과는 1000개 이상의 DIM VALUES 가 있을 때만 적용돼요. 고유 dimension 값이 1000개 미만인 dimension에 대한 topN은 순위에서 정확하고 집계에서도 정확하다고 간주될 수 있어요.

임계값은 query context의 minTopNThreshold 를 통해 기본값 1000에서 수정할 수 있으며, 쿼리별로 적용돼요.

고카디널리티, 균등 분포된 dimension을 어떤 저카디널리티, 균등 분포된 dimension으로 정렬한 상위 100을 원한다면, 데이터가 누락된 집계를 받을 수 있어요.

다시 말하면, topN의 최상의 사용 사례는 전체 결과가 균일하게 top에 있다고 확신할 수 있을 때예요. 예를 들어 특정 사이트 ID가 매일 매시간 어떤 metric의 상위 10에 있다면, 여러 날에 걸친 topN에서 정확할 가능성이 높아요. 그러나 어떤 사이트가 어떤 시간에 겨우 상위 1000 안에 들지만 전체 쿼리 세분성에서 상위 500 안에 드는 경우(예: 매우 균일한 트래픽을 가진 사이트가 매우 주기적인 데이터를 가진 사이트와 데이터셋에 섞여 있는 경우), top500 쿼리는 정확한 순위에 그 특정 사이트가 없을 수 있고, 그 특정 사이트의 집계에 대해 정확하지 않을 수 있어요.

이 섹션을 계속하기 전에, 정말로 정확한 결과가 필요한지 고려해 보세요. 정확한 결과를 얻는 것은 매우 리소스 집약적인 과정이에요. 대부분의 "유용한" 데이터 결과에서 근사 topN 알고리즘이 충분한 정확성을 제공해요.

고유 값이 1000개보다 많은 dimension에 대해 정확한 순위와 정확한 집계 topN을 얻으려는 사용자는 groupBy 쿼리를 발행하고 결과를 직접 정렬해야 해요. 이는 고카디널리티 dimension에 대해 매우 계산 집약적이에요.

고유 값이 1000개보다 많은 dimension에 대해 근사 순위 topN을 허용하지만 정확한 집계가 필요한 사용자는 두 개의 쿼리를 발행할 수 있어요. 하나는 근사 topN dimension 값을 얻는 쿼리, 다른 하나는 첫 번째 쿼리의 topN 결과만 사용하는 dimension 선택 필터가 있는 또 다른 topN이에요.

예제 첫 번째 쿼리

{    "aggregations": [         {             "fieldName": "L_QUANTITY_longSum",             "name": "L_QUANTITY_",             "type": "longSum"         }    ],    "dataSource": "tpch_year",    "dimension":"l_orderkey",    "granularity": "all",    "intervals": [        "1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z"    ],    "metric": "L_QUANTITY_",    "queryType": "topN",    "threshold": 2}

예제 두 번째 쿼리

{    "aggregations": [         {             "fieldName": "L_TAX_doubleSum",             "name": "L_TAX_",             "type": "doubleSum"         },         {             "fieldName": "L_DISCOUNT_doubleSum",             "name": "L_DISCOUNT_",             "type": "doubleSum"         },         {             "fieldName": "L_EXTENDEDPRICE_doubleSum",             "name": "L_EXTENDEDPRICE_",             "type": "doubleSum"         },         {             "fieldName": "L_QUANTITY_longSum",             "name": "L_QUANTITY_",             "type": "longSum"         },         {             "name": "count",             "type": "count"         }    ],    "dataSource": "tpch_year",    "dimension":"l_orderkey",    "filter": {        "fields": [            {                "dimension": "l_orderkey",                "type": "selector",                "value": "103136"            },            {                "dimension": "l_orderkey",                "type": "selector",                "value": "1648672"            }        ],        "type": "or"    },    "granularity": "all",    "intervals": [        "1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z"    ],    "metric": "L_QUANTITY_",    "queryType": "topN",    "threshold": 2}

더 알아보기 (Learn more)