parquet의 github 주소 : https://github.com/apache/parquet-format
아파치 파케(parquet)는 데이터 처리 프레임워크, 데이터 모델 또는 프로그래밍 언어에 관계없이 하둡 에코시스템에서 사용가능한 컬럼나 방식의 저장 포맷이다.
4-byte magic number "PAR1"
<Column 1 Chunk 1 + Column Metadata>
<Column 2 Chunk 1 + Column Metadata>
...
<Column N Chunk 1 + Column Metadata>
<Column 1 Chunk 2 + Column Metadata>
<Column 2 Chunk 2 + Column Metadata>
...
<Column N Chunk 2 + Column Metadata>
...
<Column 1 Chunk M + Column Metadata>
<Column 2 Chunk M + Column Metadata>
...
<Column N Chunk M + Column Metadata>
File Metadata
4-byte length in bytes of file metadata
4-byte magic number "PAR1"
파케 파일은 헤더, 하나이상의 block, footer로 구성된다. 헤더는 파케 포맷의 파일임을 알려주는 4바이트 매직 숫자인 PAR1만 포함하고 있다. 파일 메타 데이터는 footer에 저장된다.
footer데이터는 포맷 버전, 스키마, 추가 키-값 쌍, 파일의 모든 블록에 대한 메타데이터와 같은 정보를 포함한다.
file metadata, column (chunk) metadata, page header metadata가 존재
모두 TCompactProtocol로 직렬화된 thrift 구조를 갖는다.
Column chunks are composed of pages written back to back(연속적). The pages share a common header and readers can skip over page they are not interested in. The data for the page follows the header and can be compressed and/or encoded. The compression and encoding is specified in the page metadata.
메타데이터의 가능한 타입을 보면 String이 없다. 문자열은 UTF8 어노테이션을 가진 binary 기본 자료형으로 표현된다.
file > row group > column chunk > pag
import pyarrow.parquet as pq
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'city': ['New York', 'San Francisco', 'Los Angeles']
})
df.to_parquet('new_data.parquet')
new_file_path = "new_data.parquet"
new_parquet_file = pq.ParquetFile(new_file_path)
print("####################################################################")
print("new_parquet_file.metadata: ", new_parquet_file.metadata)
print("####################################################################")
print("new_parquet_file.schema: ", new_parquet_file.schema)
print("####################################################################")
print("new_parquet_file.statistics: ", new_parquet_file.metadata.row_group(0).column(0).statistics)
print("####################################################################")
print(new_parquet_file)
print("####################################################################")
print(df)
#결과
####################################################################
new_parquet_file.metadata: <pyarrow._parquet.FileMetaData object at 0x000001C642BB9490>
created_by: parquet-cpp-arrow version 11.0.0
num_columns: 3
num_rows: 3
num_row_groups: 1
format_version: 2.6
serialized_size: 2277
####################################################################
new_parquet_file.schema: <pyarrow._parquet.ParquetSchema object at 0x000001C64291AD40>
required group field_id=-1 schema {
optional binary field_id=-1 name (String);
optional int64 field_id=-1 age;
optional binary field_id=-1 city (String);
}
####################################################################
new_parquet_file.statistics: <pyarrow._parquet.Statistics object at 0x000001C642BB9620>
has_min_max: True
min: Alice
max: Charlie
null_count: 0
distinct_count: 0
num_values: 3
physical_type: BYTE_ARRAY
logical_type: String
converted_type (legacy): UTF8
####################################################################
<pyarrow.parquet.core.ParquetFile object at 0x000001C640B9BA50>
####################################################################
name age city
0 Alice 25 New York
1 Bob 30 San Francisco
2 Charlie 35 Los Angeles
파일의 메타데이터는 모든 칼럼 메타데이터의 시작 위치를 포함. 메타데이터에 포함된 내용은 thrift 파일에서 확인 가능.
메타데이터는 single pass writing을 허용하기위해 데이터 뒤에 작성됨.
데이터와 메타데이터가 분리되어 설계됨. 여러개의 파케파일을 하나의 메타데이터로 참조할 수 있음.
크기가 더 큰 row group은 더 큰 column chunk를 허용하므로 더 많은 순차적인 IO를 가능하게 한다. 더 큰 그룹은 write path에서 더 많은 버퍼링이 필요하다 (512MB ~ 1GB의 큰 row group을 권장). 전체 row group을 읽어야 할 수 있으므로 하나의 HDFS 블록에 완전히 맞도록 HDFS 블록 사이즈는 더 크게 설정해야 한다.
최적화된 읽기 설정은 다음과 같다.
row groups: 1GB
HDFS block size: 1GB
1 HDFS block per HDFS file
파케에서는 딕셔너리 인코딩이 디폴트.
중첩 칼럼들을 인코딩 하기 위해 파케는 정의 레벨(definition level)과 반복 레벨(repetition level)을 사용한 Dremel 인코딩 방식을 쓴다. 정의 레벨은 칼럼의 옵셔널 필드가 어느 정도 깊이로 정의되어 있는지를 나타내고(null의 위치 기준) 반복레벨은 각 필드의 반복되는 필드의 위치를 나타낸다. 정의 레벨과 반복 레벨의 최대값은 스키마에 의해 계산되는데 (얼마나 중첩되어 있는지..) 레벨을 저장하기 위한 최대 비트수를 정의한다. <- 그냥 밑에 트위터 기술 블로그를 보자...
Two encodings for the levels are supported BIT_PACKED and RLE. Only RLE is now used as it supersedes BIT_PACKED.
https://blog.twitter.com/engineering/en_us/a/2013/dremel-made-simple-with-parquet 참고
열 형식으로 저장하려면 스키마를 사용하여 데이터 구조를 설명해야함 (protocol buffers와 유사). 맵,리스트,세트와 같이 복잡한 타입이 필요없이 반복 필드와 그룹의 조합에 매핑될 수 있음.
스키마의 root는 "message" 라고 하는 필드그룹.
각 필드는 3개의 속성을 갖는다: repetition, type, name
type은 group 또는 primitive 타입(예: int, float, boolean, string)
repetition은 다음 세가지 중 하나 (맨 앞에)
아래는 address book이름의 스키마 예시
message AddressBook {
required string owner;
repeated string ownerPhoneNumbers;
repeated group contacts {
required string name;
optional string phoneNumber;
}
}
좋은 포스팅 잘 보고 갑니다. 감사합니다.