지원되는 데이터 포맷
지원되는 데이터 포맷 (Supported Data Formats)
Pinot이 지원하는 입력 포맷에서 데이터를 가져오는 방법을 보여 주는 일련의 가이드 모음집이에요.
본문
Pinot은 수집 중 다양한 인기 입력 포맷을 지원해요. 입력 포맷을 바꾸면 직렬화-역직렬화에 드는 시간을 줄여 수집을 더 빠르게 할 수 있어요.
입력 포맷 구성 (Configuring input formats)
입력 포맷을 바꾸려면 수집 잡 스펙에서 recordReaderSpec 구성을 조정하세요.
recordReaderSpec:
dataFormat: 'csv'
className: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReader'
configClassName: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReaderConfig'
configs:
key1 : 'value1'
key2 : 'value2'
구성은 다음 키로 이루어져요:
dataFormat: 소비할 데이터 포맷의 이름.className:RecordReader인터페이스를 구현하는 클래스 이름. 데이터 파싱에 사용돼요.configClassName:RecordReaderConfig인터페이스를 구현하는 클래스 이름.configs에 있는 값들을 파싱하는 데 사용돼요.configs: 포맷별 구성의 키-값 쌍. 선택 필드예요.
지원되는 입력 포맷 (Supported input formats)
Pinot은 기본적으로 여러 입력 포맷을 지원해요. 해당 리더와 관련 사용자 구성으로 포맷을 전환할 수 있어요.
CSV
dataFormat: 'csv'
className: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReader'
configClassName: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReaderConfig'
configs:
fileFormat: 'default' #should be one of default, rfc4180, excel, tdf, mysql
header: 'columnName separated by delimiter'
delimiter: ','
multiValueDelimiter: '-'
CSV 레코드 리더는 다음 구성을 지원해요:
fileFormat:default,rfc4180,excel,tdf,mysqlheader: 파일의 헤더.columnNames는 구성에 있는 구분자로 구분해야 해요.delimiter: 컬럼을 구분하는 문자.multiValueDelimiter: 단일 컬럼의 여러 값을 구분하는 문자. 이 문자를 이용해 컬럼을 리스트로 나눌 수 있어요.skipHeader: 파일의 헤더 레코드를 건너뛰기. Boolean.ignoreEmptyLines: 빈 줄 무시 (기본값으로 채우는 대신). Boolean.ignoreSurroundingSpaces: 컬럼 이름과 값 주변의 공백 무시. Boolean.quoteCharacter: CSV 파일에서 인용에 사용되는 단일 문자.recordSeparator: 입력 파일에서 레코드를 구분하는 문자. 플랫폼에 따라 기본값은\n또는\r.nullStringValue: CSV 파일에서 null을 나타내는 문자열 값. 기본값은 빈 문자열.stopOnError: Pinot이 잘못된 CSV 레코드를 만났을 때 파일 처리를 중단. Boolean. 기본값은false.
기본적으로 Pinot은 잘못된 데이터 행에서 복구해 나머지 파일을 계속 읽어요. 첫 번째 잘못된 레코드에서 배치 수집을 멈추려면 stopOnError: true로 설정하세요. Pinot은 초기화 중에도 CSV 헤더를 검증하므로, 잘못된 헤더나 첫 레코드는 빠르게 실패해요.
참고 CSV 파일에 어떤 문자로도 안정적으로 구분할 수 없는 원시 텍스트 필드가 있을 수 있어요. 이런 경우 수집 구성에서 multiValueDelimeter 필드를 명시적으로 빈 값으로 설정하세요.\
multiValueDelimiter: ''
Avro
원시 Avro 시간 논리형 값을 Pinot의 기본 변환 값 대신 사용해야 할 때 extractRawTimeValues를 사용하세요.
dataFormat: 'avro'
className: 'org.apache.pinot.plugin.inputformat.avro.AvroRecordReader'
configs:
extractRawTimeValues: true
Avro 레코드 리더는 파일의 데이터를 GenericRecord로 변환해요. Java 클래스나 .avro 파일은 필요하지 않아요. 기본적으로 extractRawTimeValues는 false이므로, Pinot은 추출 중 Avro 시간 논리형을 변환해요. date, time-millis, time-micros, timestamp-millis, timestamp-micros, timestamp-nanos에 대해 원시 Avro 정수 값을 유지하려면 extractRawTimeValues를 true로 설정하세요. decimal과 uuid는 항상 변환돼요.
Avro와 Pinot 데이터 타입 간 변환에 다음 표를 사용해요. 변환은 org.apache.avro.Conversions에 있는 공식 Avro 메서드로 수행돼요.
| Avro Data Type | Pinot Data Type | Comment |
|---|---|---|
| INT | INT | |
| LONG | LONG | |
| FLOAT | FLOAT | |
| DOUBLE | DOUBLE | |
| BOOLEAN | BOOLEAN | |
| STRING | STRING | |
| ENUM | STRING | |
| BYTES | BYTES | |
| FIXED | BYTES | |
| MAP | JSON | |
| ARRAY | JSON | |
| RECORD | JSON | |
| UNION | JSON | |
| DECIMAL | BYTES | |
| UUID | UUID | string의 logicalType: \"uuid\"와 16바이트 fixed UUID 값을 지원 |
| DATE | STRING | yyyy-MM-dd 형식 |
| TIME_MILLIS | STRING | HH:mm:ss.SSS 형식 |
| TIME_MICROS | STRING | HH:mm:ss.SSSSSS 형식 |
| TIMESTAMP_MILLIS | TIMESTAMP | |
| TIMESTAMP_MICROS | TIMESTAMP |
JSON
dataFormat: 'json'
className: 'org.apache.pinot.plugin.inputformat.json.JSONRecordReader'
배치 수집에서 dataFormat: 'json'는 JSONRecordReader를 사용하며 새 줄로 구분된 UTF-8 텍스트 JSON 파일만 읽어요.
스트림 수집에서는 org.apache.pinot.plugin.inputformat.json.JSONMessageDecoder를 사용하세요. stream.<type>.decoder.prop.jsonFormat이 설정되지 않으면 디코더는 동일한 UTF-8 텍스트 JSON 동작을 유지해요. 스트림 페이로드 포맷을 TEXT, POSTGRES_JSONB, SQLITE_JSONB, SMILE, CBOR로 고정할 수도 있고, 혼합 스트림에 대한 메시지별 감지를 선택하려면 AUTO로 설정할 수 있어요.
AUTO는 옵트인이며 페이로드에 self-describe 태그가 포함된 경우에만 CBOR을 감지해요. 와이어 포맷을 이미 안다면 AUTO 대신 명시적으로 고정하세요. Kafka 예제와 구성 키 설명은 Apache Kafka에서 스트리밍 데이터 수집과 수집 구성을 참고하세요.
BSON
dataFormat: 'bson'
className: 'org.apache.pinot.plugin.inputformat.bson.BSONRecordReader'
표준 BSON 길이 접두사와 함께 BSON 문서를 연속으로 저장하는 mongodump 방식의 파일에는 BSON 레코드 리더를 사용하세요. Pinot은 gzip 압축된 BSON 파일도 자동으로 감지·읽어요.
배치 수집에서 dataFormat: bson은 추가 configClassName 없이 BSONRecordReader로 해석돼요. 스트림 수집에서는 스트림 디코더로 org.apache.pinot.plugin.inputformat.bson.BSONMessageDecoder를 구성하세요. 각 스트림 메시지에는 정확히 하나의 BSON 문서가 포함되어야 해요.
BSONRecordExtractor는 스키마 강제(coercion) 전에 디코딩된 BSON 값을 Pinot 호환 Java 값으로 변환해요:
| BSON type | Pinot-side Java value | Notes |
|---|---|---|
ObjectId |
String |
24자리 16진수 문자열 |
DateTime |
java.sql.Timestamp |
밀리초 정밀도 |
BsonTimestamp |
java.sql.Timestamp |
초 정밀도; 초 내 서수는 버려짐 |
Decimal128 |
BigDecimal |
NaN과 Infinity는 null이 되고, 음의 0은 BigDecimal.ZERO가 됨 |
Binary |
byte[] |
UUID 바이너리 하위 타입 포함 |
| Embedded document | Map<String, Object> |
재귀적으로 변환 |
| Array | Object[] |
재귀적으로 변환 |
Thrift
dataFormat: 'thrift'
className: 'org.apache.pinot.plugin.inputformat.thrift.ThriftRecordReader'
configs:
thriftClass: 'ParserClassName'
참고 Thrift는 데이터를 파싱하기 위해
.thrift파일로 생성된 클래스가 필요해요..class파일은 Pinot의classpath에 있어야 하며, Pinot 배포 디렉터리의lib/폴더에 넣을 수 있어요.
Parquet
dataFormat: 'parquet'
className: 'org.apache.pinot.plugin.inputformat.parquet.ParquetRecordReader'
0.11.0 릴리스부터 Parquet 레코드 리더는 레코드를 읽을 때 ParquetAvroRecordReader 또는 ParquetNativeRecordReader를 사용할지 결정해요. 리더는 parquet 파일 footer에서 parquet.avro.schema 또는 avro.schema 키를 찾고, 있으면 Avro 리더를 사용해요.
잘못 구성된 경우 리더를 수동으로 바꿀 수 있어요.
dataFormat: 'parquet'
className: 'org.apache.pinot.plugin.inputformat.parquet.ParquetNativeRecordReader'
경고 DECIMAL 및 기타 parquet 네이티브 데이터 타입을 지원하려면 항상
ParquetNativeRecordReader를 사용하세요.
Parquet 시간 값을 기본 변환 값 대신 원시 정수 형태로 유지하려면 ParquetRecordReader에 extractRawTimeValues를 설정하세요.
dataFormat: 'parquet'
className: 'org.apache.pinot.plugin.inputformat.parquet.ParquetRecordReader'
configs:
extractRawTimeValues: true
extractRawTimeValues가 false(기본값)이면 Pinot은 추출 중 Parquet DATE, TIME_*, TIMESTAMP_* 값을 변환해요. 원시 정수를 유지하려면 true로 설정하세요. ParquetRecordReader를 사용할 때 Pinot은 이 구성을 선택한 기본 리더(ParquetAvroRecordReader 또는 ParquetNativeRecordReader)에 전달해요. DECIMAL과 UUID는 항상 변환돼요.
ParquetNativeRecordReader는 추출 중 원시 값을 네이티브 Pinot 호환 형태로 보존해요. 예를 들어 Parquet BOOLEAN은 문자열화되지 않고 Pinot BOOLEAN으로 남아요.
| Parquet Data Type | Pinot Data Type | Comment |
|---|---|---|
| BOOLEAN | BOOLEAN | 네이티브 boolean 값으로 보존 |
| INT96 | LONG | ParquetINT96 타입 나노초를 Pinot INT64 타입 밀리초로 변환 |
| INT64 | LONG | |
| INT32 | INT | |
| FLOAT | FLOAT | |
| DOUBLE | DOUBLE | |
| BINARY | BYTES | |
| FIXED-LEN-BYTE-ARRAY | BYTES | |
| DECIMAL | DOUBLE | |
| ENUM | STRING | |
| UTF8 | STRING | |
| REPEATED | MULTIVALUE/MAP (represented as MV | parquet 원본 타입이 LIST면 MULTIVALUE 컬럼으로, 그 외에는 MAP 컬럼으로 변환 |
ParquetAvroRecordReader의 타입 변환은 위의 Avro 섹션을 참고하세요.
LIST 및 MAP 래퍼 추출
Parquet LIST와 MAP 래퍼 구조체는 ParquetNativeRecordReader와 ParquetAvroRecordReader로 수집할 때 이제 올바르게 언랩(unwrap)돼요. 이전에는 스키마로 식별된 LIST/MAP 컬럼의 래퍼 요소가 데이터에 노출됐어요:
array<string>필드는 이전에[{\"element\": \"abc\"}, {\"element\": \"xyz\"}]로 반환됐지만, 이제 올바르게[\"abc\", \"xyz\"]로 반환돼요.map<string,string>필드는 이전에{\"key_value\":[{\"key\":\"k\",\"value\":\"v\"}]}로 반환됐지만, 이제 올바르게{\"k\":\"v\"}로 반환돼요.
element라는 이름의 실제 구조체 필드는 보존되고, 스키마로 식별된 LIST·MAP 래퍼만 정규화돼요. 리더는 표준 3단계 Parquet LIST 인코딩과 레거시 2단계 인코딩(반복 원시, 반복 다중 필드 그룹, 또는 element가 아닌 반복 단일 필드 그룹)을 모두 지원해요.
후방 비호환성: 이전 깨진 형태를 우회하던 수집 파이프라인이나 변환 표현식(예: 배열 컬럼에 data 대신 data.element 선택)이 있다면 해당 쿼리와 변환을 갱신해야 해요.
MAP 순서: Parquet 자체는 원본 MAP 항목 순서를 보존하지 않아요. Pinot은 값을 직렬화할 때 맵 키를 정렬해 수집된 MAP·JSON 출력을 표준화하므로, 쿼리 결과는 결정적이지만 원본 삽입 순서는 보존되지 않아요. 원본 쌍 순서가 중요하다면 필드를 LIST<STRUCT<key, value>>로 모델링하세요.
ORC
dataFormat: 'orc'
className: 'org.apache.pinot.plugin.inputformat.orc.ORCRecordReader'
ORC 레코드 리더는 다음 데이터 타입을 지원해요:
| ORC Data Type | Java Data Type |
|---|---|
| BOOLEAN | String |
| SHORT | Integer |
| INT | Integer |
| LONG | Integer |
| FLOAT | Float |
| DOUBLE | Double |
| STRING | String |
| VARCHAR | String |
| CHAR | String |
| LIST | Object[] |
| MAP | Map<Object, Object> |
| DATE | Long |
| TIMESTAMP | Long |
| BINARY | byte[] |
| BYTE | Integer |
참고 LIST와 MAP 타입에서 객체는 Pinot이 지원하는 데이터 타입 중 하나에만 속해야 해요.
Protocol Buffers
dataFormat: 'proto'
className: 'org.apache.pinot.plugin.inputformat.protobuf.ProtoBufRecordReader'
configs:
descriptorFile: 'file:///path/to/sample.desc'
리더는 파일의 데이터를 역직렬화하기 위해 descriptor 파일이 필요해요. .proto 파일에서 descriptor 파일(.desc)을 다음 명령으로 생성할 수 있어요:
protoc --include_imports --descriptor_set_out=/absolute/path/to/output.desc /absolute/path/to/input.proto
Apache Arrow
Arrow 입력 포맷 플러그인은 Apache Arrow IPC 포맷의 데이터 읽기를 지원해요. 이는 Arrow 형식 출력을 생성하는 시스템에서 데이터를 수집할 때 유용해요.
성공
pinot-arrow플러그인은 표준 Pinot 바이너리 배포(tarball 및 Docker 이미지)에 포함돼 있어요. Apache Arrow 포맷을 데이터 수집에 쓰기 위한 추가 설치 단계는 필요 없어요.
배치 수집
Arrow IPC 파일에서 배치 수집하려면:
dataFormat: 'arrow'
className: 'org.apache.pinot.plugin.inputformat.arrow.ArrowRecordReader'
ArrowRecordReader는 배치 수집용 Arrow IPC 파일을 읽어요. Arrow IPC 파일은 seek 가능한 채널이 필요하므로 gzip 압축은 지원되지 않아요.
원시 Arrow 시간 값을 Pinot의 기본 변환 값 대신 보존하려면 ArrowRecordReader에 extractRawTimeValues를 설정하세요:
dataFormat: 'arrow'
className: 'org.apache.pinot.plugin.inputformat.arrow.ArrowRecordReader'
configs:
extractRawTimeValues: true
extractRawTimeValues가 false(기본값)이면 Pinot은 추출 중 Arrow Date, Time, Timestamp 값을 변환해요. 원시 정수를 유지하려면 true로 설정하세요. Date는 epoch 이후 일 수로, Time과 Timestamp는 스키마가 선언한 Arrow 단위로 유지돼요.
직접 세그먼트 생성
표준 배치 수집 잡 스펙은 계속 ArrowRecordReader를 사용해요. 애플리케이션 코드에서 세그먼트를 직접 만들면 Pinot은 SegmentIndexCreationDriverImpl.init(config, columnReaderFactory)를 통한 선택적 Arrow 컬럼-major 경로도 제공해요.
디스크의 Arrow IPC 파일에는 ArrowFileColumnReaderFactory를 사용하세요:
SegmentIndexCreationDriverImpl driver = new SegmentIndexCreationDriverImpl();
try (ArrowFileColumnReaderFactory factory = new ArrowFileColumnReaderFactory(arrowFile)) {
driver.init(config, factory);
driver.build();
}
파일 기반 팩토리는 세그먼트 구축 중 한 번에 하나의 Arrow 레코드 배치를 읽어요. 직접 통합을 위해 다음 구성 키도 노출해요:
arrowAllocatorLimit: 파일 기반 컬럼-major 경로의 최대 Arrow 오프힙 할당자 크기(바이트). 기본값은268435456(256 MB).extractRawTimeValues: ArrowDate,Time,Timestamp값을 Pinot의 기본 변환 값 대신 원시 정수로 유지. 기본값은false.
프로세스 내에서 ArrowReader와 할당자를 이미 관리한다면 ArrowColumnReaderFactory를 대신 사용하세요. 이 경로는 동일한 extractRawTimeValues 동작을 지원하지만, 할당자 크기는 호출자가 관리해야 해요.
스트림 수집
스트림 수집에서 Arrow 디코더는 Arrow 컬럼형 배치를 Pinot 행으로 변환해요:
stream.kafka.decoder.class.name=org.apache.pinot.plugin.inputformat.arrow.ArrowMessageDecoder
구성 속성:
| Property | Default | Description |
|---|---|---|
arrow.allocator.limit |
268435456 (256 MB) | Arrow 오프힙 할당자의 메모리 제한(바이트) |
extractRawTimeValues |
false |
Arrow Date, Time, Timestamp 값을 Pinot의 기본 변환 값 대신 원시 정수로 유지 |
Arrow 타입 변환은 자동으로 처리돼요: UTF-8 텍스트는 String이 되고, Date는 LocalDate, Time은 LocalTime, Timestamp는 Timestamp가 되며, Arrow Map은 평탄화된 Map<String, Object>로, Arrow List는 Object[]로 변환돼요. 딕셔너리 인코딩 컬럼은 추출 전 논리형에 따라 디코딩돼요.
각 Arrow Kafka 메시지는 완전한 IPC 스트림을 포함해야 해요. 빈 배치는 건너뛰고, 단일 행 배치는 하나의 Pinot 행으로 수집되며, 다중 행 배치는 여러 Pinot 행으로 펼쳐져요.