3. Parquet格式深入

在金融数据工程中,Parquet 之所以能成为事实标准,核心在于它把“列式存储”从概念落到了可精确控制的物理结构上。理解 Parquet 不能停留在“列存、压缩率高”这种标签式认知,而要深入到文件内部的层级组织:文件由行组构成,行组由列块构成,列块由页构成,页内再通过编码与压缩落地。只有看清这条结构链,才能解释为什么同样的数据、同样的查询,性能可能相差数倍。

一、核心原理:从文件到页的五层结构

Parquet 文件采用“先水平切分、再垂直切分”的混合组织方式。最外层是文件,内部按行数或大小切分为若干行组(Row Group);每个行组内,每一列独立形成一个列块(Column Chunk);列块内部再切分为多个页(Page),页是编码和压缩的最小单位。文件尾部保存 Footer,其中记录 Schema、每个行组、每个列块的元数据与统计信息。读取时先读 Footer,再按需定位行组与列块,这就是“谓词下推”和“列裁剪”的物理基础。

Parquet 文件物理结构层级 Parquet File Row Group 0 Column Chunk trade_id Page 0 Page 1 Page 2 Column Chunk price Page 0 Page 1 Row Group 1 Column Chunk trade_id Page 0 Page 1 Column Chunk price Page 0 Footer:Schema + 行组/列块元数据 + 统计信息(min/max/null_count)

二、编码与压缩:页内的两级优化

页是 Parquet 真正做优化的地方。写入时,先对页内数据做编码,再做压缩。编码负责减少值的表示冗余,压缩负责减少字节冗余,两者叠加才能获得高压缩比。

  • 字典编码(Dictionary):适合低基数列,如交易所代码、证券类型、买卖方向。把重复值映射为整数索引,页内只存索引和字典。
  • RLE / Bit-Packing:常与字典编码配合,对索引序列做游程或位打包,进一步压缩重复模式。
  • Delta 编码:适合单调递增或近似有序的数值列,如时间戳、序列号,存储相邻差值而非原值。
  • Plain 编码:无编码兜底,适合高基数、无明显规律的列。

压缩算法方面,金融场景常见选择如下:

压缩算法压缩率解压速度典型场景
SNAPPY中等很快热数据、交互式查询
ZSTD高快温数据、归档与查询兼顾
GZIP较高较慢冷数据、长期存储
LZ4较低极快低延迟写入与扫描
金融行情数据写入频繁、查询延迟敏感,通常优先 SNAPPY 或 ZSTD;历史归档可切到 GZIP 换取更高压缩率。

三、统计信息与谓词下推:让查询跳过数据

Parquet 在每个列块和页级别记录统计信息,典型包括 min、max、null_count,部分实现还记录 distinct_count。查询引擎读取 Footer 后,可以先判断某个行组是否可能包含满足条件的数据:如果查询条件是 price > 100,而某列块的 max=95,该列块可直接跳过,无需读取页数据。这就是谓词下推(Predicate Pushdown)。

谓词下推的效果取决于数据排序与行组划分。若数据按 trade_date 有序写入,按日期过滤时能跳过大量行组;若数据完全乱序,每个行组的 min/max 范围都很宽,下推收益会明显下降。因此,写入时的排序与分区策略,直接决定读取时的下推效率。

# 使用 PyArrow 查看 Parquet 元数据与统计信息
import pyarrow.parquet as pq

pf = pq.ParquetFile("trades.parquet")
print(pf.metadata.num_row_groups)

rg = pf.metadata.row_group(0)
col = rg.column(1)  # price 列
print(col.statistics.min, col.statistics.max, col.statistics.null_count)

四、案例与误区复盘

某量化团队将逐笔成交数据写入 Parquet,初期未排序,按 symbol 过滤查询延迟很高。排查发现每个行组的 symbol 列 min/max 覆盖了全部代码,谓词下推几乎无效。调整为按 symbol 排序并控制行组大小后,相同查询扫描行组数下降约 80%。

常见误区:只调压缩算法,不调行组大小与排序。行组过大,统计信息粒度粗,下推跳过的数据少;行组过小,Footer 与元数据膨胀,读取开销上升。通常建议单行组 128MB~512MB,并结合查询模式做排序或分区。

另一个误区是忽视页级统计。列块级统计只能跳过整个列块,而页级统计可以在列块内部进一步跳过页。对于宽范围但局部集中的数据,页级下推能带来额外收益。实际调优时,应同时关注行组大小、页大小、排序键与压缩算法四个参数,而不是孤立地调其中一个。

掌握 Parquet 的文件结构、行组、列块、页、编码、压缩、统计信息与谓词下推,本质上是掌握“写入时如何组织、读取时如何跳过”的完整闭环。金融数据工程中的性能问题,大多能在这条结构链上找到答案。