⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
文件格式

处理 Parquet 文件

DuckDB 对 Parquet 文件提供了高级支持,包括直接查询 Parquet 文件。在决定是直接查询这些文件还是先将其加载到数据库时,你需要考虑几个因素。

查询 Parquet 文件的理由

基本统计信息的可用性:Parquet 文件使用列式存储格式,并包含诸如区域映射 (zonemaps) 等基本统计信息。得益于这些特性,DuckDB 可以利用针对 Parquet 文件的投影和过滤下推等优化。因此,结合了投影、过滤和聚合的工作负载在 Parquet 文件上运行时往往表现良好。

存储考量:从 Parquet 文件加载数据将需要与 DuckDB 数据库文件大致相同的空间。因此,如果磁盘空间有限,直接对 Parquet 文件运行查询是值得的。

不建议查询 Parquet 文件的理由

缺乏高级统计信息:DuckDB 数据库格式具有 Parquet 文件所没有的 HyperLogLog 统计信息。这些信息提高了基数估计的准确性,如果查询包含大量的连接 (join) 操作,这一点尤为重要。

提示:如果你发现 DuckDB 在 Parquet 文件上生成的连接顺序不是最优的,请尝试将 Parquet 文件加载到 DuckDB 表中。改进后的统计信息很可能会帮助获得更好的连接顺序。

重复查询:如果你计划在同一个数据集上运行多次查询,那么将数据加载到 DuckDB 是值得的。查询速度总是会快一些,随着时间的推移,这可以摊销掉初始加载的时间。

解压时间长:一些 Parquet 文件使用 gzip 等重量级压缩算法进行压缩。在这种情况下,每次访问文件时查询 Parquet 文件都需要昂贵的解压时间。与此同时,轻量级压缩方法(如 Snappy、LZ4 和 zstd)的解压速度更快。你可以使用 parquet_metadata 函数来找出所使用的压缩算法。

微基准测试:在 DuckDB 数据库与 Parquet 上运行 TPC-H

TPC-H 基准测试中的查询在 Parquet 文件上的运行速度大约比在 DuckDB 数据库上慢 1.1-5.0 倍。

最佳实践:如果你有足够的存储空间,且工作负载包含大量连接和/或计划在同一数据集上运行多个查询,请先将 Parquet 文件加载到数据库中。Parquet 文件中的压缩算法和行组大小对性能影响很大:请使用 parquet_metadata 函数来研究这些因素。

行组大小的影响

DuckDB 在行组大小为 10 万到 100 万行之间的 Parquet 文件上运行效果最佳。其原因是 DuckDB 只能跨行组进行并行处理——因此,如果一个 Parquet 文件只有一个巨大的行组,它只能由单个线程处理。你可以使用 parquet_metadata 函数来查看 Parquet 文件有多少个行组。在编写 Parquet 文件时,请使用 row_group_size 选项。

微基准测试:在不同行组大小下运行聚合查询

我们在不同行组大小(选择在 960 到 1,966,080 之间)的 Parquet 文件上运行了一个简单的聚合查询。结果如下。

行组大小 执行时间
960 8.77 秒
1920 8.95 秒
3840 4.33 秒
7680 2.35 秒
15360 1.58 秒
30720 1.17 秒
61440 0.94 秒
122880 0.87 秒
245760 0.93 秒
491520 0.95 秒
983040 0.97 秒
1966080 0.88 秒

结果表明,小于 5,000 的行组大小会产生强烈的负面影响,导致运行时间比理想行组大小长 5-10 倍以上,而 5,000 到 20,000 之间的行组大小距离最佳性能仍有 1.5-2.5 倍的差距。在行组大小超过 100,000 后,差异很小:最佳和最差运行时间之间的差距约为 10%。

Parquet 文件大小

DuckDB 也可以跨多个 Parquet 文件进行并行处理。建议所有文件的总行组数至少要与 CPU 线程数一样多。例如,在一台拥有 10 个线程的机器上,10 个各含 1 个行组的文件或 1 个含 10 个行组的文件都能实现完全并行。保持单个 Parquet 文件的大小适中也是有益的。

最佳实践:每个单独 Parquet 文件的理想大小范围在 100 MB 到 10 GB 之间。

用于过滤下推的 Hive 分区

当使用过滤条件查询许多文件时,可以通过使用 Hive 格式的文件夹结构沿过滤条件中使用的列对数据进行分区来提高性能。DuckDB 将只需要读取满足过滤条件的文件夹和文件。在查询远程文件时,这尤其有用。

关于读写 Parquet 文件的更多提示

有关读取和写入 Parquet 文件的提示,请参阅 Parquet 提示页面

加载 CSV 文件

CSV 文件通常以压缩格式(如 GZIP 归档,即 .csv.gz)分发。DuckDB 可以即时解压这些文件。事实上,由于减少了 IO,这通常比先解压文件再加载它们要快。

模式 加载时间
从 GZIP 压缩的 CSV 文件(.csv.gz)加载 107.1 秒
解压(使用并行 gunzip)并从解压后的 CSV 文件加载 121.3 秒

加载大量小型 CSV 文件

CSV 读取器会对所有文件运行 CSV 嗅探器 (sniffer)。对于大量小文件,这可能会导致不必要的高开销。一种潜在的加速优化是关闭嗅探器。假设所有文件都具有相同的 CSV 方言和列名/类型,请按如下方式获取嗅探器选项

.mode line
SELECT Prompt FROM sniff_csv('part-0001.csv');
Prompt = FROM read_csv('file_path.csv', auto_detect=false, delim=',', quote='"', escape='"', new_line='\n', skip=0, header=true, columns={'hello': 'BIGINT', 'world': 'VARCHAR'});

然后,你可以调整 read_csv 命令,例如应用 文件名扩展 (globbing),并使用嗅探器检测到的其余选项运行该命令

FROM read_csv('part-*.csv', auto_detect=false, delim=',', quote='"', escape='"', new_line='\n', skip=0, header=true, columns={'hello': 'BIGINT', 'world': 'VARCHAR'});
© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南