跳到主要内容

Parquet 使用技巧

下面汇总了一些处理 Parquet 文件时的实用建议。

读取 Parquet 的建议

schema 不同时使用 union_by_name

union_by_name 可用于统一列不一致或存在缺列的文件 schema。缺失列会自动填充为 NULL

SELECT *
FROM read_parquet('flights*.parquet', union_by_name = true);

写出 Parquet 的建议

读取时使用 glob 模式,或写出时采用 Hive 分区结构,都是透明处理多文件的好方法。

启用 PER_THREAD_OUTPUT

如果不关心最终 Parquet 文件数量,每线程写一个文件通常能显著提升性能:

COPY
(FROM generate_series(10_000_000))
TO 'test.parquet'
(FORMAT parquet, PER_THREAD_OUTPUT);

选择 ROW_GROUP_SIZE

ROW_GROUP_SIZE 参数指定 Parquet row group 的最小行数,最小值为 Goose 向量大小 2,048,默认值为 122,880。 Parquet row group 可视为“行分区”,每个分区包含该数据集每一列对应的 column chunk。

压缩算法按 row group 生效,因此 row group 越大,通常压缩机会越多。 但 row group 越大也意味着流式写出时每个线程在 flush 前需要缓存更多内存数据。 另一方面,较小 row group 也有优势:Goose 可在同一文件内并行读取 row group,并结合谓词下推仅扫描元数据范围匹配查询 WHERE 条件的组。不过,每个组的元数据读取也会带来一定额外开销。

经验法则是:每个文件的 row group 数量至少应不小于用于查询该文件的 CPU 线程数。 超过线程数的更多 row group 有利于高选择性查询,但对必须全表扫描(如聚合)的查询可能会变慢。

若要以不同 row group 大小写出查询结果,可执行:

COPY
(FROM generate_series(100_000))
TO 'row-groups.parquet'
(FORMAT parquet, ROW_GROUP_SIZE 100_000);

ROW_GROUPS_PER_FILE 选项

ROW_GROUPS_PER_FILE 参数会在当前文件达到指定 row group 数后创建新 Parquet 文件。

COPY
(FROM generate_series(100_000))
TO 'output-directory'
(FORMAT parquet, ROW_GROUP_SIZE 20_000, ROW_GROUPS_PER_FILE 2);

若有多个线程同时写入,为减少锁竞争,单个文件的 row group 数可能会略高于设定值——行为类似 FILE_SIZE_BYTES。 但若设置了 PER_THREAD_OUTPUT,每个文件仅由一个线程写入,此时该限制会更精确。

更多建议请参阅性能指南中的“文件格式”章节