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,每个文件仅由一个线程写入,此时该限制会更精确。
更多建议请参阅性能指南中的“文件格式”章节。