# 개요
전통적인 행 기반(row-oriented) 저장 방식(CSV, JSON, RDBMS heap file)은 OLTP(트랜잭션 처리)에 최적화되어 있지만,
분석 쿼리(OLAP)에서는 치명적인 비효율이 발생합니다.
예를 들어 1억 개의 row에서 단 2개의 컬럼만 집계할 때도, 행 기반 포맷은 전체 row를 읽어야 합니다.
Parquet은 이 문제를 열 지향(columnar) 저장 방식으로 해결합니다.
Parquet은 복잡한 중첩 데이터 구조를 염두에 두고 처음부터 설계되었으며,
Dremel 논문에 설명된 record shredding and assembly algorithm 을 사용합니다.
저희는 이러한 접근 방식이 중첩된 네임스페이스를 단순히 평면화하는 것보다 우수하다고 생각합니다.
[기본 철학 ]
- Columnar Storage (열 지향 저장): 행(Row)이 아닌 열(Column) 단위로 데이터를 저장하는 방식.
- Schema-on-read: 데이터 파일 안에 스키마 정보가 포함되어 있어, 읽을 때 데이터 구조를 바로 파악함.
- Metadata: 파일 끝(Footer)에 저장된 메타데이터 (데이터 타입, 통계 정보 등).
- Binary Format: 텍스트가 아닌 이진 형식으로 저장되어 컴퓨터가 처리하기 훨씬 빠름.

# 용어
- Block (HDFS block): HDFS의 블록을 의미합니다.
- File : 파일에 대한 메타데이터를 포함해야 하는 HDFS 파일입니다.
- Row group: 데이터를 행 단위로 논리적으로 수평 분할한 묶음. Row Group 에는 물리적 구조가 없습니다. (보통 128MB~1GB 단위)
- Column chunk: 특정 컬럼을 모아놓은 데이터 덩어리입니다. 이러한 청크는 특정 Row Group 에 속하며 파일에서 연속적으로 유지됨이 보장됩니다.
- Page: 가장작은 데이터 단위 , 압축과 인코딩이 실제로 일어나는 지점. 컬럼 단위의 데이터 덩어리들이 페이지 단위로 나뉩니다.
파케이는 Row group,Column chunk,Page 3개의 계층으로 구성됩니다.
📌 Row Group(논리적 분할)
거대한 테이블 전체를 한 번에 열 단위로 쪼개는 것은 불가능합니다.
📌 Column Chunk( 물리적 분할)
하나의 로우 그룹 내부로 들어가면, 비로소 데이터가 속성(Column)별로 모여서 저장됩니다.
즉, 로우 그룹 내의 '나이' 데이터만 뭉쳐 놓은 덩어리, '이름' 데이터만 뭉쳐 놓은 덩어리가 각각의 컬럼 청크가 됩니다.
📌 Page( 데이터 최소 단위)
컬럼 청크는 다시 1MB~8MB 크기의 여러 '페이지'로 잘게 쪼개집니다. 가장 중요한 점은 이 페이지마다 헤더(Header)가 존재하며,
이 헤더에 해당 페이지 내 데이터의 통계 정보가 기록된다는 것입니다.
[Unit of parallelization 병렬처리 단위]
MapReduce - File/Row Group( 파일/행 그룹화) 단위 수행
IO - Column chunk(컬럼 청크) 단위 수행
Encoding/Compression - Page(페이지) 단위 수행
# 파케이 파일 포맷의 아키텍처

분산 쿼리 엔진(Impala, Spark)이 HDFS나 S3에서 파케이 파일을 읽을 때의 동작 방식은 다음과 같습니다.
1. 처음 4바이트 검증: 파일 시작점의 PAR1을 읽어 정상 파일인지 확인합니다.
2. 파일의 맨 끝으로 Jump (Seek): 데이터를 처음부터 순차적으로 읽지 않습니다. 파일의 맨 끝에서 8바이트를 뒤로 돌아갑니다.
3. 메타데이터 위치 역추적: 마지막 4바이트가 PAR1인지 확인하고, 그 앞의 4바이트(Footer Length)를 읽어냅니다.
이 길이를 통해 Footer 메타데이터가 어디서 시작하는지 정확한 Offset을 알아냅니다.
4.메타데이터만 먼저 로드: 파일 전체를 스캔하는 대신, Footer에 있는 메타데이터만 메모리에 올립니다.
5. Predicate Pushdown (조건절 푸쉬다운): 메타데이터 안에는 각 컬럼 덩어리(Row Group)의 최소값(Min)과 최대값(Max)이 적혀 있습니다.
만약, 쿼리가 WHERE age > 50인데, 특정 Row Group의 통계가 Min=10, Max=30이라면 엔진은 그 거대한 데이터 블록을
아예 디스크에서 읽지 않고 스킵(Skip)해 버립니다.
[쿼리 엔진]
WHERE age > 50
↓ pushdown
[Parquet Reader]
Row Group 1: max(age)=40 → SKIP
Row Group 2: max(age)=80 → READ
Row Group 3: max(age)=35 → SKIP
<자주 하는 오해>
"컬럼 지향이면 특정 행 하나를 읽을 때도 빠르지 않나?"
오히려 반대다. 한 행의 모든 컬럼을 읽으려면 각 컬럼 Chunk에서 해당 위치를 찾아야 하니 랜덤 I/O가 여러 번 발생한다.
Parquet은 점 조회(point lookup)에는 적합하지 않다. OLTP 워크로드에 Parquet을 쓰면 오히려 느려진다.
Metadata 메타데이터
metadata: file metadata, and page header metadata.
- 파일 메타데이터: 파케이 푸터에 들어가 있다. 파케이 파일을 탐색시 오프셋 및 크기 정보등의 메타데이터를 제공한다.
- 페이지 헤더 메타데이터: 페이지 데이터와 함께 저장되며, 데이터 읽기 및 디코딩에 사용된다.
모든 스리프트 구조는 TCompactProtocol을 사용하여 직렬화됩니다. (Serialization)
전체 이러한 구조에 대한 정의는 Parquet Thrift definition 에 나와 있습니다.
⇒ 데이터 구조를 저장하거나 네트워크로 전송할 때, 빈 공간 없이 꽉꽉 눌러 담아서(용량을 최소화해서) 바이트(Byte) 형태로 변환한다는 뜻
직렬화 (Serialization)
메모리에서 돌아가고 있는 복잡한 데이터 구조(객체)를 파일로 저장하거나 네트워크로 전송할 수 있도록 일렬로 줄 세우는(바이트 스트림으로 바꾸는) 작업을 말합니다.
Thrift Structure (스리프트 구조체)
아파치 스리프트(Apache Thrift)는 자바, 파이썬, C++ 등 서로 다른 프로그래밍 언어들끼리 데이터를 쉽게 주고받기 위해 만든 데이터 규격 및 통신 프레임워크입니다.
TCompactProtocol (T-콤팩트-프로토콜)
스리프트에서 데이터를 직렬화할 때 사용하는 가장 효율적인 텍스트/바이트 압축 방식 중 하나입니다.
파일 메타데이터
version, schema, num_rows, row_groups, key_value_metadata, created_by 등 메타데이터가 저장된다.


페이지 헤더
타입, 비압축 페이지 사이즈, 압축 페이지 사이즈 등이 저장된다.

# Data Pages 데이터 페이지
데이터 페이지의 경우 page header에 다음 3개의 정보가 연속적으로 인코딩됩니다. 데이터 페이지에는 여백(패딩)이 허용되지 않습니다.
1. repetition levels data 반복 수준 데이터
2. definition levels data 정의 수준 데이터
3. encoded values 인코딩된 값
헤더에 지정된 uncompressed_page_size 값은 세 부분을 모두 합친 값입니다.
즉, uncompressed_page_size = repetition levels data + definition levels data + encoded values
Compression 압축
-UNCOMPRESSED: 아무 작업도 수행하지 않는 코덱입니다. 데이터는 압축되지 않은 상태로 유지됩니다.
- SNAPPY : Snappy 압축 형식을 기반으로 하는 코덱입니다. ( Snappy compression format)
- GZIP : RFC 1952에 정의된 GZIP 형식(밀접하게 관련된 "zlib" 또는 "deflate" 형식이 아님)을 기반으로 하는 코덱입니다.
Encodings 인코딩

Parquet이 사용하는 주요 인코딩 방식은 다음과 같다.
Dictionary Encoding은 가장 강력한 인코딩 중 하나다.
컬럼에서 고유값(distinct value)을 모아 사전(dictionary)을 만들고, 실제 데이터는 사전의 인덱스만 저장한다.
"KR"이 100만 번 나오는 컬럼이라면, 사전에 "KR=0"을 한 번만 저장하고 나머지는 전부 0이라는 숫자만 저장한다. 카디널리티(고유값 수)가 낮은 컬럼에 매우 효과적이다.
RLE(Run-Length Encoding)는 같은 값이 연속으로 나올 때 "이 값이 N번 반복된다"는 형태로 압축한다.
정렬된 데이터나 반복 패턴이 많은 데이터에서 극적인 압축 효과를 낸다.
Delta Encoding은 숫자 시퀀스에서 값 자체 대신 이전 값과의 차이(delta)를 저장한다.
타임스탬프 컬럼처럼 값이 단조 증가하는 경우, 델타가 거의 일정하므로 아주 적은 비트로 표현할 수 있다.
<동작 흐름>
Parquet은 컬럼의 통계를 보고 인코딩을 자동으로 선택한다.
초기에는 Dictionary Encoding을 시도하다가 고유값이 일정 임계치를 넘으면(사전이 너무 커지면) Plain Encoding으로 fallback한다.
<자주 하는 오해>
압축 코덱(Snappy vs Zstd)만 신경 쓰고 인코딩을 간과하는 경우가 많다.
그러나 인코딩은 데이터 의미를 이용하는 반면 압축은 바이트 패턴만 보기 때문에, 인코딩을 잘 하면 압축 전 데이터 자체가 훨씬 작아진다.
좋은 인코딩 + 가벼운 압축이, 나쁜 인코딩 + 강한 압축보다 결과가 좋고 CPU도 덜 쓴다.
Encryption 암호화
Parquet 파일은 페이지, 페이지 헤더, 열 인덱스, 오프셋 인덱스, 블룸 필터 헤더 및 비트셋, 푸터와 같이 개별적으로 직렬화된 구성 요소로 이루어져 있습니다.
Parquet 암호화 메커니즘은 이러한 구성 요소를 "모듈"로 지칭하고 각 모듈을 개별적으로 암호화합니다.
따라서 푸터를 가져와 복호화하거나, 필요한 페이지의 오프셋을 찾고, 페이지를 가져와 데이터를 복호화하는 것이 가능합니다.
이 문서에서 "푸터"라는 용어는 항상 일반적인 Parquet 푸터 FileMetaData구조와 그 안에 포함된 필드(행 그룹/열 청크)를 의미합니다.
Parquet 암호화 알고리즘은 대칭 암호화를 위한 표준 AES 암호를 기반으로 합니다.
Checksumming 체크섬
모든 종류의 페이지에 대해 개별 체크섬을 계산할 수 있습니다.
이를 통해 HDFS 파일 수준에서 체크섬 계산을 비활성화하여 단일 행 조회를 더욱 효율적으로 지원할 수 있습니다.
체크섬은 페이지의 직렬화된 바이너리 표현(페이지 헤더 제외)에 대해 표준 CRC32 알고리즘(예: GZip에서 사용됨)을 사용하여 계산됩니다.
Column Chunks 컬럼 청크
컬럼 청크는 연속으로 작성된 페이지들로 구성되어 있습니다.
페이지들은 공통 헤더를 공유하며, readers는 관심 없는 페이지를 건너뛸 수 있습니다.
페이지의 데이터는 헤더 다음에 위치하며 압축 및/또는 인코딩할 수 있습니다.
압축과 인코딩은 페이지 메타데이터에 명시되어 있습니다.
column chunk might be partly or completely dictionary encoded.
It means that dictionary indexes are saved in the data pages instead of the actual values.
The actual values are stored in the dictionary page. See details in Encodings.md.
The dictionary page must be placed at the first position of the column chunk.
컬럼 청크는 부분적으로 또는 전적으로 사전 인코딩될 수 있습니다.
즉, 사전 인덱스가 실제 값 대신 데이터 페이지에 저장된다는 뜻입니다.
실제 값은 사전 페이지에 저장됩니다. 자세한 내용은 Encodings.md 에서 확인하세요.
사전 페이지는 열의 첫 번째 위치에 배치되어야 합니다.
Error Recovery 오류 복구
If the file metadata is corrupt, the file is lost.
파일 메타데이터가 손상되면 파일이 사라집니다.
If the column metadata is corrupt, that column chunk is lost (but column chunks for this column in other row groups are okay).
열 메타데이터가 손상되면 해당 열 청크가 사라집니다.(하지만, 다른 행 그룹의 이 열의 컬럼 청크는 괜찮습니다).
If a page header is corrupt, the remaining pages in that chunk are lost. If the data within a page is corrupt, that page is lost.
페이지 헤더가 손상되면 해당 청크의 나머지 페이지들이 손실됩니다. 페이지 내 데이터가 손상되면 해당 페이지가 사라집니다.
The file will be more resilient to corruption with smaller row groups.
행 그룹의 크기가 작을수록 파일 손상에 대한 복원력이 높아집니다.
참고 자료 : https://parquet.apache.org/docs/
'3. Data Engineering > ㅤ📘 데이터 웨어하우스' 카테고리의 다른 글
| SCD(Slowly Changing Dimensions) 개념 (0) | 2026.03.08 |
|---|---|
| iceberg , 아이스버그 개념 및 아키텍처 (0) | 2026.02.28 |
| Airflow, 에어플로우의 기본 개념 (0) | 2026.02.25 |
| Oozie, 우지의 기본 개념 (0) | 2026.02.25 |
| impala, 임팔라 엔진의 데이터 검색 (1) | 2026.02.23 |