구성 추천 엔진

구성 추천 엔진 (Configuration Recommendation Engine)

이 페이지는 배포에 적합한 구성을 추천하는 자동화 메커니즘을 설명해요.

출처: 문서

본문

개요 (Overview)

추천 엔진(Recommendation Engine)은 Pinot 테이블에 대한 최적의 구성 옵션을 추천하는 규칙 기반(rule-based) 엔진이에요. 현재 엔진이 다루는 구성 옵션은 대부분 TableConfig 관련(예: 인덱스, 실시간 구성)이에요. TableConfig의 모든 구성 옵션이 현재 다뤄지는 것은 아니라는 점에 유의하세요. 현재 다뤄지는 옵션은 다음 표와 같아요.

규칙 구성 엔티티 구성 이름 적용 테이블 유형
Kafka Partitions Kafka - num.partitions 실시간 (Real-time)
Table Partitioning Table Config - tableIndexConfig→segmentPartitionConfig 실시간 & 오프라인
Inverted Sorted Index Joint Table Config - tableIndexConfig→invertedIndexColumns - tableIndexConfig→sortedColumn 실시간 & 오프라인
NoDictionary OnHeapDictionary Joint Table Config - tableIndexConfig→noDictionaryColumns - tableIndexConfig→onHeapDictionaryColumns 실시간 & 오프라인
Bloom Filter Table Config - tableIndexConfig→bloomFilterColumns 실시간 & 오프라인
Varied Length Dictionary Table Config - tableIndexConfig→variedLengthDictionaryColumns 실시간 & 오프라인
Segment Size Segment Build & Push Job 다음에 대한 추천: - 세그먼트 크기(바이트) - 세그먼트 수 - 세그먼트당 행 수 오프라인 (Offline)
Aggregate Metrics Table Config - tableIndexConfig→aggregateMetrics 실시간
Real-time Provisioning Table Config - tableIndexConfig→streamConfigs→realtime.segment.flush.threshold.time - tableIndexConfig→streamConfigs→realtime.segment.flush.threshold.segment.size 실시간
Real-time Provisioning Host Management 메모리 소비 측면에서 필요한 호스트 수 실시간

추천 엔진은 신규 테이블과 기존 테이블 모두의 구성 파라미터를 최적화하는 데 사용할 수 있어요. 추천 엔진은 최적에 가까운 구성을 생성하려고 하므로, 사용자는 아는 한 입력 정보를 충실히 제공하는 것이 크게 권장돼요. 정보가 완전히 정확하지 않아도 괜찮아요. 하지만 무작위/임의적이고 불완전한 정보는 추천 엔진의 알고리즘에 도움이 되지 않아요.

RealtimeProvisioningHelper 섹션도 참고하세요.

엔진 사용 방법 (How to use the engine)

이 엔진은 현재 Pinot Controller의 REST 엔드포인트로 접근할 수 있어요. /tables/recommender 경로 아래의 PUT 엔드포인트로, json을 입력받아 json을 출력해요. Pinot Web UI의 Swagger REST API 섹션에서 시도해볼 수 있어요.

입력 (Input)

입력은 json 형식으로 제공해야 해요. 입력의 서로 다른 필드는 다음 네 그룹으로 분류할 수 있어요.

1. 데이터 특성 (Data Characteristics)

데이터 특성은 "schema" 필드에 정의돼요. 이 필드의 내용은 각 컬럼의 정의에 삽입되는 추가 메타데이터가 있는 Pinot Schema의 확장 버전이에요:

  • Cardinality (카디널리티): 이 차원의 총 고유 값 수. 가능한 최고의 추천을 위해 아는 한도 내에서 카디널리티를 제공하세요.
  • numValuesPerEntry: 다중 값(multi-value) 컬럼에만 해당하며, 컬럼 값당 평균 값 수예요. 다음에 주의하세요:
    • 다중 값 컬럼의 경우 numValuesPerEntry 외에 singleValueField를 false로 정의해야 해요.
    • 단일 값(single-value) 컬럼의 경우 numValuesPerEntry와 singleValueField를 지정하지 않아야 해요.
    • 메트릭 컬럼은 다중 값으로 정의할 수 없어요.
    • BYTES 타입 컬럼은 다중 값으로 정의할 수 없어요.
  • averageLength: BYTES 또는 STRING 데이터 타입에만 해당하며, 컬럼 값의 평균 길이예요.
  • UUID 필드: 엔진이 샘플 데이터를 생성할 때 dataType이 UUID로 설정된 스키마 컬럼이 지원돼요. 생성된 Avro 샘플 데이터는 표준 UUID 문자열을 사용하고, UUID 컬럼은 논리 타입 uuid인 Avro string으로 표현돼요.

2. 테이블 특성 (Table Characteristics)

  • 실시간/하이브리드 테이블의 경우,
    • Kafka partitions [선택] Kafka 팀이 제안한 파티션 수로 이 필드를 채우세요. 제공하지 않으면 엔진이 Kafka 토픽에 대한 최적 파티션 수를 추천하려고 시도해요.
    • Kafka messages per second [필수] 초당 Kafka 토픽으로 가는 평균 메시지 수.
  • 오프라인/하이브리드 테이블의 경우 다음이 필요해요:
    • Records per push
  • 모든 테이블에 다음이 필요해요:
    • Expected QPS
    • Latency SLA in ms
    • Query pattern 아는 한도 내에서 채워야 해요. 성능에 영향을 주는 많은 구성(예: 인덱스, 파티셔닝, 정렬)이 쿼리 패턴으로 생성돼요.

3. 규칙 (Rules)

이 섹션에서는 서로 다른 구성에 대한 추천을 생성하는 규칙들을 설명해요:

  • Segment Size - 이 규칙은 오프라인 테이블에 대해 다음 파라미터를 추천해요: 1) 세그먼트 수, 2) 각 세그먼트의 레코드 수, 3) 각 세그먼트의 크기. 신규 테이블의 경우 규칙은 제공된 데이터 특성을 사용해 이들 파라미터의 최적 값을 찾아요. 테이블이 이미 프로덕션에 있다면 기존 세그먼트에서 다음 정보를 얻을 수 있어요: 세그먼트의 행 수와 세그먼트 크기. 그런 다음 이를 segment size 규칙의 actualSegmentSize와 numRowsInActualSegment 파라미터로 추가하세요. 이에 대한 예시는 Overrides 섹션에서 볼 수 있어요.
  • Kafka Partitions - Kafka 파티션 수가 아직 결정/제공되지 않았다면 이 규칙이 값을 추천해요. 최적 파티션 수를 만들려면 토픽의 총 메시지 속도(토픽의 모든 파티션에 걸친 초당 메시지 수)가 필요해요.
  • Inverted Sorted Index Join - 어떤 컬럼에 정렬 인덱스(sort index)나 역인덱스(inverted index)를 둬야 하는지 추천해요.
  • Table Partitioning - 테이블의 실시간·오프라인 부분의 파티셔닝 파라미터는 보통 같지만, 일부 사용 사례는 다른 파라미터를 가질 수 있어요. 실시간 부분의 경우 이 규칙은 Kafka Partitions 규칙의 출력을 그대로 반영해요. 오프라인의 경우 이 규칙은 최적 세그먼트 수를 기반으로 결정되는 파티션 수 파라미터 값을 추천해요 — Segment Size 규칙에서 계산 방법을 참고하세요. 이 규칙은 또한 쿼리 패턴을 검토해 어느 컬럼이 더 자주(물론 쿼리 패턴의 가중치에 비례해서) IN 또는 EQUALITY 필터에 나타나는지 찾아 파티셔닝에 가장 좋은 성능을 주는 컬럼을 추천해요.
  • Bloom Filter - 블룸 필터는 EQUALITY 필터에 자주 나타나는 컬럼에 유용해요. 이 규칙은 어떤 컬럼에 블룸 필터를 둬야 하는지 추천해요. 해당 블룸 필터의 메모리 사용량이 크므로 고카디널리티 컬럼은 건너뛰어요.
  • NoDictionary OnHeapDictionary Joint - 어떤 컬럼을 NoDictionary 컬럼으로 정의하고 어떤 컬럼에 on-heap 사전 인덱스를 둬야 하는지 추천해요. 사전 인코딩은 효율적인 쿼리 처리와 저장 공간 절약에 도움이 돼요. 하지만 불필요한 사전 생성은 저장 공간과 때로는 성능 측면에서 추가 비용이 될 수 있어요. 사용자가 제공한 쿼리 패턴을 기반으로 규칙은 사전 인코딩이 유익한 최적의 컬럼 집합을 찾으려고 해요. on-heap 사전 부분의 경우, 많이 쿼리되고 카디널리티가 낮은(그래서 on-heap 메모리 사용량이 허용 가능한 수준으로 작은) 컬럼이 on-heap 사전을 가질 수 있어요. on-heap 사전은 더 나은 쿼리 실행 성능을 낼 수 있어요.
  • Varied Length Dictionary - 이 규칙은 가변 길이 데이터 타입, 즉 STRING 또는 BYTES의 경우 가변 길이 사전(varied length dictionary)을 사용할 것을 추천해요. 이 사전을 사용하면 더 나은 성능을 얻을 수 있어요.
  • Flag Queries - 유효하지 않은 쿼리 패턴에 플래그를 표시해요.
  • Aggregate Metrics - 이 규칙은 제공된 쿼리를 검사해 테이블 구성의 'AggregateMetrics' 플래그 값을 제안해요. 선택(selection) 컬럼을 보고 모두 SUM 함수라면 플래그가 true여야 하고, 그렇지 않으면 false예요. 또한 SUM 함수에 나타나는 모든 컬럼 이름이 실제로 메트릭 컬럼인지도 확인해요.
  • Real-time Provisioning - 실시간 테이블 프로비저닝에 유용한 몇 가지 추천을 제공해요. 구체적으로 제공된 데이터 특성과 Kafka 적재 속도를 기반으로 최적 세그먼트 크기, 호스트당 총 메모리 사용량, 호스트당 consuming 세그먼트의 메모리 사용량에 대한 통찰을 제공해요. 이 규칙의 궁극적인 목표는 적절한 실시간 세그먼트 크기로 이어지는 요구 소비 기간(consumption duration)과 요구 호스트 수를 찾는 거예요.

모든 규칙은 기본적으로 실행돼요. 실행할 규칙을 선택할 수 있는 옵션이 있어요.

다음 규칙은 실시간 또는 하이브리드 테이블에만 적용된다는 점을 유의하세요:

  • Kafka Partitions
  • Real-time Provisioning
  • Aggregate Metrics

그리고 다음 규칙은 오프라인 또는 하이브리드 테이블에만 적용돼요:

  • Segment Size

4. 오버라이드 (Overrides)

각 규칙은 기본값을 가진 몇 가지 파라미터가 있을 수 있어요. 이 기본값들을 변경해 규칙의 동작을 바꿀 수 있어요. 예를 들어, Recommend Table Partitioning 규칙에서 저 QPS 테이블에 대해서는 파티셔닝을 추천하지 않아요. 기본 동작을 바꾸려면 다음을 사용할 수 있어요:

"partitionRuleParams": {
  "THRESHOLD_MIN_QPS_PARTITION":300
}

두 번째 예로 Segment Size 규칙을 봅시다. 이 규칙은 제공된 데이터 특성을 기반으로 세그먼트 하나를 생성해요. 파라미터 numRowsInGeneratedSegment가 생성된 세그먼트의 크기를 제어해요. 파라미터 desiredSegmentSizeMB는 원하는 세그먼트 크기를 지정해요. 기본값은 50,000행과 500MB예요:

"segmentSizeRuleParams": {
    "desiredSegmentSizeMB":200,
    "numRowsInGeneratedSegment":10000
}

또는 테이블이 이미 프로덕션에 있고 실제 세그먼트 크기와 실제 세그먼트의 행 수를 안다면 다음을 할 수 있어요:

"segmentSizeRuleParams": {
    "desiredSegmentSizeMB": 200,
    "actualSegmentSizeMB": 250,
    "numRowsInActualSegment": 3500000
}

파라미터의 자세한 목록과 용법은 소스 코드와 이 wiki 페이지에 상세히 문서화돼 있어요.

입력 json의 마지막 항목은 오버라이드된 구성(overridden configs)이에요. 추천 엔진에 일부 구성 값을 추천하지 말고 대신 해당 구성에 대해 사용자가 제공한 값을 사용하라고 지시할 수 있어요. 이렇게 하면 규칙 엔진이 사용자가 고정한 파라미터를 기준으로 다른 파라미터의 최적 값을 찾으려고 해요. 예를 들어, 역인덱스 컬럼과 범위 인덱스 컬럼을 다음과 같이 지정하고 싶다고 말할 수 있어요:

"overWrittenConfigs": {
    "indexConfig":{
        "invertedIndexColumns":["a","b"],
        "rangeIndexColumns":["f"]
    }
}

출력 (Output)

현재 엔진은 다음 구성을 추천해요:

  • 인덱싱 구성 (Indexing Config):
    • Inverted Index Columns 역(비트맵) 인덱스를 적용할 컬럼.
    • Primary Sorted Column 세그먼트 생성 중 모든 데이터를 정렬하는 데 쓰는 하나의 컬럼.
    • Bloom Filter Columns 블룸 필터를 추가할 컬럼.
    • No Dictionary Columns 사전을 추가하지 않을 컬럼.
    • On Heap Dictionary Columns on-heap에 둘 사전.
    • Varied Length Dictionary Columns 가변 길이 사전을 만들 컬럼.
  • 세그먼트 파티션 구성 (Segment Partition Config):
    • Number of Kafka Partitions 제공된 Kafka 파티션 수와 같을 값.
    • Primary Partitioning Column 세그먼트 생성 중 모든 데이터를 파티셔닝하는 데 쓰는 하나의 컬럼.
    • Number of Real-time Partitions 테이블의 실시간 측 파티션 수.
    • Number of Offline Partitions 테이블의 오프라인 측 파티션 수.
  • 세그먼트 크기 추천 (Segment Size Recommendations):
    • Segment Size 오프라인 세그먼트에 권장되는 크기(바이트).
    • Number of Segments 오프라인 세그먼트에 권장되는 수.
    • Number of Rows per Segment 오프라인 세그먼트에 권장되는 행 수.
  • 실시간 프로비저닝 추천 (Real-time Provisioning Recommendations):
    • 다음 각 추천은 기본적으로 호스트 수와 소비 시간(consumption hours)의 2D 행렬이에요. 목표는 Kafka 적재를 처리하는 데 몇 개의 실시간 호스트가 필요한지 사용자에게 통찰을 주는 거예요. 또한 두 streamConfig 파라미터 realtime.segment.flush.threshold.segment.size와 realtime.segment.flush.threshold.time의 값을 제공된 정보에 따라 결정할 수 있어요.
      • Optimal Segment Size numHost와 numConsumptionHour의 각 조합에 대한 실시간 세그먼트 크기 행렬.
      • Consuming Memory per Host consuming 세그먼트만의 메모리 크기 행렬.
      • Total Memory Used per Host consuming을 포함한 모든 실시간 세그먼트의 총 메모리 크기 행렬.
  • 플래그된 쿼리 (Flagged Queries):
    • 유효하지 않거나 비싼 쿼리에 플래그를 표시해요.

가까운 시일 내에 더 많은 규칙을 추가할 계획이에요.

샘플 실행 (Sample run)

입력:

{
  "schema":{
    "dimensionFieldSpecs": [
      {
        "cardinality": 10000,
        "dataType": "LONG",
        "name": "studentID"
      },
      {
        "averageLength": 8,
        "cardinality": 2000,
        "dataType": "STRING",
        "name": "firstName"
      },
      {
        "averageLength": 12,
        "cardinality": 2000,
        "dataType": "STRING",
        "name": "lastName"
      },
      {
        "averageLength": 6,
        "cardinality": 2,
        "dataType": "STRING",
        "name": "gender"
      },
      {
        "averageLength": 25,
        "cardinality": 100,
        "dataType": "STRING",
        "name": "subject"
      }
    ],
    "metricFieldSpecs": [
      {
        "cardinality": 5000,
        "dataType": "FLOAT",
        "name": "score"
      }
    ],
    "schemaName": "transcript"
  },
  "queriesWithWeights":{
    "select subject, count(*) from transcript where score > 3 and gender = 'MALE' group by subject": 0.5,
    "select subject, score from transcript where firstName = 'Tsubasa' and lastName = 'Oozora'": 0.5
  },
  "tableType": "OFFLINE",
  "numRecordsPerPush":100000000,
  "qps": 5,
  "latencySLA": 1000,
  "rulesToExecute": {
    "recommendRealtimeProvisioning": false
  }
}

출력:

{
  "aggregateMetrics": false,
  "flaggedQueries": {
    "flaggedQueries": {}
  },
  "indexConfig": {
    "bloomFilterColumns": [],
    "invertedIndexColumns": [
      "gender"
    ],
    "noDictionaryColumns": [
      "studentID",
      "score"
    ],
    "onHeapDictionaryColumns": [],
    "rangeIndexColumns": [
      "score"
    ],
    "sortedColumn": "firstName",
    "sortedColumnOverwritten": true,
    "variedLengthDictionaryColumns": [
      "firstName",
      "lastName",
      "gender",
      "subject"
    ]
  },
  "partitionConfig": {
    "numKafkaPartitions": 0,
    "numPartitionsOffline": 1,
    "numPartitionsOfflineOverwritten": false,
    "numPartitionsRealtime": 1,
    "numPartitionsRealtimeOverwritten": false,
    "partitionDimension": "",
    "partitionDimensionOverwritten": false
  },
  "realtimeProvisioningRecommendations": {},
  "segmentSizeRecommendations": {
    "message": null,
    "numRowsPerSegment": 33333333,
    "numSegments": 3,
    "segmentSize": 482736662
  }
}

더 알아보기 (Learn more)