Apache Parquet 文件格式(File Format)
概述
本文件与 Thrift 定义应结合阅读,以便深入理解 Parquet 文件格式。
文件结构
Parquet 文件的基本结构如下:
4 字节魔数 "PAR1"
<列 1 - 块 1>
<列 2 - 块 1>
...
<列 N - 块 1>
<列 1 - 块 2>
<列 2 - 块 2>
...
<列 N - 块 2>
...
<列 1 - 块 M>
<列 2 - 块 M>
...
<列 N - 块 M>
文件元数据
4 字节小端序的元数据长度(字节)
4 字节魔数 "PAR1"
在上述示例中,表格包含 N
个列,并被拆分为 M
个行组(Row Groups)。
文件元数据(Metadata)记录了所有列块(Column Chunk)的起始位置。
更多关于元数据内容的细节,可以在 Thrift 定义中找到。
元数据与数据分离的设计
- 写入顺序:文件元数据是在所有数据写入后追加到文件末尾的,以支持单次遍历写入。
- 读取顺序:读取器(Reader)应先读取文件元数据,以确定所需的列块位置,然后按顺序读取这些列块。
- 优势:
- 这种格式设计使得元数据与数据分离,可以实现:
- 跨多个文件拆分列存储,提高灵活性。
- 使用单个元数据文件引用多个 Parquet 文件,适用于分布式存储。
最佳实践
- 优化列存储
- 由于 Parquet 采用列式存储,在写入时尽可能按列批量处理数据,以减少 I/O 负担。
-
例如,在 Spark 或 Pandas 处理中,先对数据按照列进行批量分割再写入 Parquet。
-
合理控制行组(Row Group)大小
- 小行组(Row Group) 会导致过多的元数据索引,影响查询性能。
- 大行组(Row Group) 则可能导致单个读取任务过大,影响并行处理效率。
-
建议:
- 典型的行组大小:128MB - 512MB,根据具体查询场景调整。
-
利用字典编码与压缩
- Parquet 支持字典编码(Dictionary Encoding)和多种压缩算法(如 Snappy、Gzip、ZSTD)。
-
建议:
- 对重复值较多的列,启用字典编码。
- 选择适合的压缩算法,例如:
- Snappy(默认):解压快,适用于查询密集型任务。
- Gzip:压缩率高,但解压速度较慢,适合存储优化场景。
-
合理选择分区(Partitioning)
- Parquet 可以结合 Hive 风格的分区存储(如
/year=2024/month=02/data.parquet
)。 -
建议:
- 选择查询频繁的字段作为分区键(如
date
、category
)。 - 避免过度分区,过小的分区可能导致过多小文件,影响查询性能。
- 选择查询频繁的字段作为分区键(如
-
使用 Predicate Pushdown(谓词下推)
- Parquet 支持谓词下推,即查询引擎会根据元数据过滤不必要的数据块,减少扫描的数据量。
- 示例(SQL 方式):
sql SELECT * FROM orders WHERE order_date >= '2024-01-01'
- 建议:
- 尽量使用基于列的过滤条件(如
WHERE column = 'value'
),让查询引擎更高效地跳过无关数据。
- 尽量使用基于列的过滤条件(如