테이블
테이블 (Table)
Apache Pinot의 테이블 구성 요소를 살펴보는 페이지예요. 테이블은 Pinot 클러스터에서 데이터를 구성하고 관리하는 기본 빌딩 블록으로, 세그먼트, 인덱싱, 테넌트, 하이브리드 테이블 등을 포함한 개념을 다룹니다.
출처: Table
본문
Pinot는 데이터를 테이블에 저장해요. Pinot 테이블은 개념적으로 행과 칼럼이 있는 관계형 데이터베이스 테이블과 동일해요. 칼럼은 같은 이름과 데이터 타입을 가지며, 이를 테이블의 스키마라고 해요.
Pinot 스키마는 JSON 파일로 정의돼요. 스키마 정의가 별도 파일에 있기 때문에 여러 테이블이 단일 스키마를 공유할 수 있어요. 각 테이블은 고유한 이름, 인덱싱 전략, 파티셔닝, 데이터 소스 및 기타 메타데이터를 가질 수 있어요.
Pinot 테이블 유형은 다음과 같아요:
- real-time: Apache Kafka® 같은 스트리밍 소스에서 데이터를 수집
- offline: 배치 소스에서 데이터를 로드
- hybrid: 배치 소스와 스트리밍 소스 둘 다에서 데이터를 로드
Pinot는 테이블을 여러 세그먼트로 나누고 이 세그먼트를 Hadoop Distributed File System (HDFS) 같은 deep-store와 Pinot 서버에 저장해요.
Pinot 클러스터에서 테이블은 Helix resource로 모델링되고, 테이블의 각 세그먼트는 Helix Partition으로 모델링돼요.
💡 Pinot의 테이블 이름은 일반적인 명명 규칙을 따르며, 예를 들어 이름을 문자로 시작하고 밑줄로 끝나지 않으며 영숫자만 사용해요.
Pinot는 다음 유형의 테이블을 지원해요:
| 유형 | 설명 |
|---|---|
| Offline | 오프라인 테이블은 외부 데이터 저장소에서 미리 빌드된 Pinot 세그먼트를 수집하며 일반적으로 배치 수집에 사용 |
| Real-time | 실시간 테이블은 스트림(예: Kafka)에서 데이터를 수집하고 소비된 데이터로 세그먼트를 구축 |
| Hybrid | 하이브리드 Pinot 테이블은 내부적으로 실시간 및 오프라인 테이블을 모두 가짐. 기본적으로 Pinot의 모든 테이블은 하이브리드 |
💡 데이터베이스를 쿼리하는 사용자는 테이블의 유형을 알 필요가 없어요. 쿼리에서 테이블 이름만 지정하면 돼요.
예를 들어 오프라인 테이블
myTable_OFFLINE, 실시간 테이블myTable_REALTIME, 또는 둘 다 포함하는 하이브리드 테이블 중 어떤 것이 있어도 쿼리는 다음과 같아요:select count(*) from myTable
테이블 설정은 이름, 유형, 인덱싱, 라우팅, 보존 같은 테이블 속성을 정의하는 데 사용돼요. JSON 형식으로 작성되며 테이블 스키마와 함께 Zookeeper에 저장돼요.
다음 속성을 사용해 테이블을 더 빠르게 또는 더 가볍게 만들 수 있어요:
- Segment
- Indexing
- Tenants
- Partitioning — offline, realtime, hybrid 테이블에 대한 자세한 내용은 Configuring Table Partitioning 참조
세그먼트 (Segments)
테이블은 세그먼트로 알려진 작은 데이터 청크로 구성돼요. Pinot가 세그먼트를 생성하고 관리하는 방법에 대해 자세히 알아보려면 여기를 참조하세요.
오프라인 테이블의 경우 세그먼트는 Pinot 외부에서 빌드되고 Spark나 Hadoop 같은 분산 실행기로 업로드돼요. 자세한 내용은 배치 수집을 참조하세요.
실시간 테이블의 경우 세그먼트는 Pinot 내부에서 특정 간격으로 빌드돼요. 실시간 세그먼트에 대해 다음을 조정할 수 있어요.
플러시 (Flush)
Pinot 실시간 소비자는 데이터를 수집하고 세그먼트를 만든 다음 인메모리 세그먼트를 디스크로 플러시해요. Pinot는 다음 방식으로 세그먼트를 언제 플러시할지 구성할 수 있어요:
- 소비된 행 수 (Number of consumed rows): 스트림에서 지정된 수의 행을 소비한 후 Pinot가 세그먼트를 디스크에 영속화해요.
- 세그먼트당 행 수 (Number of rows per segment): Pinot가 소비해야 할 행 수를 학습한 다음 추정해요. 학습 단계는 행 수를 100,000(이 값은 변경 가능)으로 설정해 시작하고 적절한 세그먼트 크기에 도달하도록 조정해요. Pinot는 진행하면서 추정을 교정하므로 학습 단계 중에 세그먼트 크기가 올바른 크기를 크게 초과할 수 있어요. 이 값을 쿼리 성능을 최적화하도록 설정해야 해요.
- 최대 대기 시간 (Max time duration to wait): Pinot 소비자는 구성된 시간 동안 기다린 후 세그먼트를 디스크에 영속화해요.
복제본 (Replicas)
세그먼트는 더 높은 가용성을 제공하기 위해 여러 복제본을 가질 수 있어요. CLI를 사용해 테이블 세그먼트에 대한 복제본 수를 구성할 수 있어요.
완료 모드 (Completion Mode)
기본적으로 승자가 아닌 서버(non-winner server)의 인메모리 세그먼트가 커밋된 세그먼트와 동일하면 non-winner 서버는 세그먼트를 빌드하고 교체해요. 사용 가능한 세그먼트가 커밋된 세그먼트와 동일하지 않으면 서버는 controller에서 커밋된 세그먼트를 그냥 다운로드해요.
그러나 특정 시나리오에서는 세그먼트 빌드가 메모리 집약적일 수 있어요. 이런 경우 non-committer 서버가 다시 빌드하는 대신 controller에서 세그먼트를 그냥 다운로드하도록 강제하고 싶을 수 있어요. 테이블 설정에서 completionMode: "DOWNLOAD"를 설정하면 돼요.
자세한 내용은 Completion Config를 참조하세요.
다운로드 스킴 (Download Scheme)
Pinot 서버는 완료 후 HDFS 같은 deep store에서 세그먼트를 다운로드하지 못할 수 있어요. 하지만 서버가 deep store 대신 피어 서버에서 이 세그먼트를 다운로드하도록 구성할 수 있어요. 현재 HTTP와 HTTPS 다운로드 스킴만 지원돼요. gRPC/Thrift 같은 더 많은 방법이 향후 추가될 계획이에요.
실시간 수집 중 피어 세그먼트 다운로드에 대한 자세한 내용은 deep store 우회 설계 문서를 참조하세요.
인덱싱 (Indexing)
테이블에 여러 인덱스를 생성해 쿼리 성능을 높일 수 있어요. 지원되는 인덱스 유형은 다음과 같아요:
- Forward Index
- 비트 압축을 사용한 딕셔너리 인코딩 포워드 인덱스
- 원시 값 포워드 인덱스
- 런-렝스 인코딩을 사용한 정렬 포워드 인덱스
- Inverted Index
- 비트맵 inverted 인덱스
- 정렬 inverted 인덱스
- Star-tree Index
- Range Index
- Text Index
- Geospatial
각 인덱싱 메커니즘과 해당 설정에 대한 자세한 내용은 Indexing을 참조하세요.
칼럼에 Bloomfilter를 설정해 쿼리를 더 빠르게 만들 수 있어요. 더 빠른 쿼리를 위해 세그먼트를 온힙 메모리 대신 오프힙에 유지할 수도 있어요.
사전 집계 (Pre-aggregation)
세그먼트 크기를 줄이기 위해 실시간 스트림 데이터를 소비하면서 집계해요. 모든 차원과 시간 칼럼에 대해 같은 값을 가진 모든 행의 메트릭 칼럼 값을 더하고 세그먼트에 단일 행을 만들어요. 이 기능은 REALTIME 테이블에서만 사용할 수 있어요.
지원되는 유일한 집계는 SUM이에요. 사전 집계할 칼럼은 다음 요구 사항을 충족해야 해요:
- 모든 메트릭이
noDictionaryColumns에 나열되어야 함 - 다중 값 차원이 없어야 함
- 모든 차원 칼럼은 설정에서
noDictionaryColumns로 나타나더라도 딕셔너리가 있는 것으로 취급됨
다음 테이블 설정 스니펫은 실시간 수집 중 사전 집계를 활성화하는 예를 보여줘요:
"tableIndexConfig": {
"noDictionaryColumns": ["metric1", "metric2"],
"aggregateMetrics": true,
...
}
테넌트 (Tenants)
각 테이블은 테넌트와 연결돼요. 세그먼트는 자신과 같은 테넌트를 가진 서버에 존재해요. 자세한 내용은 Tenant를 참조하세요.
선택적으로 세그먼트 상태에 따라 테이블이 다른 테넌트의 서버로 이동해야 하는지 재정의할 수 있어요. 아래 예제는 소비(consuming) 및 완료(completed) 세그먼트에 대한 태그를 재정의하기 위해 실시간 테이블의 tenants 섹션 아래에 tagOverrideConfig를 추가해요.
"broker": "brokerTenantName",
"server": "serverTenantName",
"tagOverrideConfig" : {
"realtimeConsuming" : "serverTenantName_REALTIME"
"realtimeCompleted" : "serverTenantName_OFFLINE"
}
}
위 예제에서 소비 세그먼트는 여전히 serverTenantName_REALTIME 호스트에 할당되지만, 완료되면 세그먼트는 serverTenantName_OFFLINE으로 이동해요.
이 섹션에서 어떤 태그의 전체 이름도 지정할 수 있어요. 예를 들어 이 테이블의 완료 세그먼트를 allTables_COMPLETED로 태그된 Pinot 서버에 두기로 결정할 수 있어요. 자세한 내용은 완료 세그먼트 이동 섹션을 참조하세요.
하이브리드 테이블 (Hybrid table)
하이브리드 테이블은 같은 이름을 공유하는 오프라인 테이블과 실시간 테이블 두 개로 구성된 테이블이에요. 하이브리드 테이블에서 오프라인 세그먼트는 주기적으로 푸시될 수 있어요. 오프라인 테이블의 보존은 세그먼트가 주기적으로 들어오기 때문에 높은 값으로 설정할 수 있는 반면, 실시간 부분의 보존은 작게 설정할 수 있어요.
오프라인 세그먼트가 최근 시간 범위를 다루도록 푸시되면 브로커가 자동으로 해당 시간 범위의 세그먼트에 오프라인 테이블을 사용하도록 전환하고, 오프라인 테이블에서 사용할 수 없는 데이터에만 실시간 테이블을 사용해요.
하이브리드 테이블에 대한 시간 경계가 어떻게 동작하는지 배우려면 Broker를 참조하세요.
하이브리드 테이블의 전형적인 사용 사례는 매일 중복 제거되고 정리된 데이터를 오프라인 테이블에 푸시하면서 실시간 데이터가 도착하는 대로 소비하는 거예요. 데이터는 오프라인 테이블에 수년 동안 남을 수 있는 반면, 실시간 데이터는 며칠마다 정리돼요.
예제 (Examples)
자신의 데이터에 대한 테이블 설정을 만들거나, 가능한 모든 배치/스트리밍 테이블은 examples에서 볼 수 있어요.
사전 요구 사항
오프라인 테이블 생성 (Offline table creation)
Docker:
docker run \
--network=pinot-demo \
--name pinot-batch-table-creation \
${PINOT_IMAGE} AddTable \
-schemaFile examples/batch/airlineStats/airlineStats_schema.json \
-tableConfigFile examples/batch/airlineStats/airlineStats_offline_table_config.json \
-controllerHost pinot-controller \
-controllerPort 9000 \
-exec
샘플 콘솔 출력
Executing command: AddTable -tableConfigFile examples/batch/airlineStats/airlineStats_offline_table_config.json -schemaFile examples/batch/airlineStats/airlineStats_schema.json -controllerHost pinot-controller -controllerPort 9000 -exec
Sending request: http://pinot-controller:9000/schemas to controller: a413b0013806, version: Unknown
{"status":"Table airlineStats_OFFLINE succesfully added"}
런처 스크립트 사용:
bin/pinot-admin.sh AddTable \
-schemaFile examples/batch/airlineStats/airlineStats_schema.json \
-tableConfigFile examples/batch/airlineStats/airlineStats_offline_table_config.json \
-exec
curl:
# add schema
curl -F schemaName=@airlineStats_schema.json localhost:9000/schemas
# add table
curl -i -X POST -H 'Content-Type: application/json' \
-d @airlineStats_offline_table_config.json localhost:9000/tables
Rest API에서 테이블 설정을 확인해 성공적으로 업로드됐는지 확인하세요.
스트리밍 테이블 생성 (Streaming table creation)
Docker:
Kafka 시작
docker run \
--network pinot-demo --name=kafka \
-e KAFKA_NODE_ID=1 \
-e KAFKA_PROCESS_ROLES=broker,controller \
-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 \
-e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER \
-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \
-e KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 \
-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
-e CLUSTER_ID=MkU3OEVBNTcwNTJENDM2Qk \
-d apache/kafka:4.0.0
Kafka 토픽 생성
docker exec \
-t kafka \
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafka:9092 \
--partitions=1 --replication-factor=1 \
--create --topic flights-realtime
스트리밍 테이블 생성
docker run \
--network=pinot-demo \
--name pinot-streaming-table-creation \
${PINOT_IMAGE} AddTable \
-schemaFile examples/stream/airlineStats/airlineStats_schema.json \
-tableConfigFile examples/docker/table-configs/airlineStats_realtime_table_config.json \
-controllerHost pinot-controller \
-controllerPort 9000 \
-exec
샘플 출력
Executing command: AddTable -tableConfigFile examples/docker/table-configs/airlineStats_realtime_table_config.json -schemaFile examples/stream/airlineStats/airlineStats_schema.json -controllerHost pinot-controller -controllerPort 9000 -exec
Sending request: http://pinot-controller:9000/schemas to controller: 8fbe601012f3, version: Unknown
{"status":"Table airlineStats_REALTIME succesfully added"}
런처 스크립트 사용:
Kafka-Zookeeper 시작
bin/pinot-admin.sh StartZookeeper -zkPort 2181
Kafka 시작
bin/pinot-admin.sh StartKafka -zkAddress=localhost:2181/kafka -port 19092
스트림 테이블 생성
bin/pinot-admin.sh AddTable \
-schemaFile examples/stream/airlineStats/airlineStats_schema.json \
-tableConfigFile examples/stream/airlineStats/airlineStats_realtime_table_config.json \
-exec
Rest API에서 테이블 설정을 확인해 성공적으로 업로드됐는지 확인하세요.
논리 테이블 (Logical table)
논리 테이블은 여러 물리 테이블에 걸쳐 통합된 쿼리 인터페이스를 제공해요. 지리적 파티셔닝, 테이블 샤딩 전략, 또는 복잡한 테이블 계층 위에 추상화 레이어를 만들 때 유용해요.
자세한 내용은 논리 테이블을 참조하세요.
하이브리드 테이블 생성 (Hybrid table creation)
하이브리드 테이블을 만들려면 오프라인과 실시간 테이블을 각각 만들어야 해요. 별도의 하이브리드 테이블을 만들 필요는 없어요.
"OFFLINE": {
"tableName": "pinotTable",
"tableType": "OFFLINE",
"segmentsConfig": {
...
},
"tableIndexConfig": {
...
},
"tenants": {
"broker": "myBrokerTenant",
"server": "myServerTenant"
},
"metadata": {
...
}
},
"REALTIME": {
"tableName": "pinotTable",
"tableType": "REALTIME",
"segmentsConfig": {
...
},
"tableIndexConfig": {
...
"streamConfigs": {
...
},
},
"tenants": {
"broker": "myBrokerTenant",
"server": "myServerTenant"
},
"metadata": {
...
}
}
}