Parquet 是什么
Parquet 是面向数据分析的列式文件格式:数据按列而不是按行存放,同一列的值连续存储、类型一致,压缩效率远高于行式文件。它自带 schema 与列级统计信息,Spark、Hive、Trino、Flink 等引擎都能直接读写,是数据湖里落地明细层最常用的格式。
为什么它扫得快
查询只需要读到用到的列,其他列可以不落盘;每个 row group 与 page 都记录了 min/max 等统计值,引擎可以据此跳过整块不命中的数据。配合按日期分区的目录结构,一次查询可能只需要读全表几十分之一的数据。
基本信息
| 特点 | 列式存储、自带 schema、列级统计、支持嵌套结构 |
|---|---|
| 粒度 | 文件 → row group(常见 128MB 级)→ page |
| 压缩 | 列内数据同质,压缩率通常高于文本格式数倍 |
| 生态 | Spark、Hive、Trino、Flink、Presto 均可直接读写 |
| 写入方式 | 通常不接受行级 UPDATE,改动需重写文件 |
典型使用场景
- 数仓明细层(DWD)落地:列式读取 + 分区裁剪,报表扫描量最小
- 日志明细入湖:字段多但每次只查少数字段,列式优势最明显
- 需要保留 schema 的数据交换:自带类型信息,下游不必再猜字段类型
- 冷数据长期归档:压缩率高,存储成本明显低于 JSON 或 CSV
- 作为 Iceberg、Hudi 等表格式的底层文件格式使用
查看文件 schema 与统计信息
查看文件 schema 与统计信息
# 用 pyarrow 查看字段与类型
python -c "import pyarrow.parquet as pq; print(pq.read_schema('part-00000.parquet'))"
# 查看元数据:行数、row group 数量与每列大小
python -c "import pyarrow.parquet as pq; m=pq.ParquetFile('part-00000.parquet').metadata; print(m.num_rows, m.num_row_groups)"Spark 按分区写入
Spark 按分区写入
# 按 dt 分区写出,查询时可直接按 dt 过滤,实现目录级裁剪
# maxRecordsPerFile 控制单个文件的行数,避免一次写出上千个小文件
df.write.mode('overwrite') \
.partitionBy('dt') \
.option('maxRecordsPerFile', 2000000) \
.parquet('s3a://bucket/dwd/orders')文件结构读法
| 概念 | 含义 | 常见取值 |
|---|---|---|
| row group | 行组,列式统计与跳过的基本单位 | 128MB 级,可调 |
| page | 行组内的更小存储单元 | 按列切分,受编码影响 |
| 列级统计 | 每列的 min/max/null 计数 | 用于谓词下推与数据跳过 |
| footer | 文件末尾的 schema 与元数据 | 读取时先解析 footer,收尾才能提交 |
单文件控制在百 MB 级、避免海量小文件,是扫描性能与写入成本的平衡点:文件太小会让元数据与文件打开开销占主导,太大则单个 task 并行度下降。
Parquet 不支持高效行级更新:频繁 UPDATE 会不断重写整个文件,需要 upsert 能力的场景应使用 Iceberg、Hudi 等表格式,或按分区整体覆盖。