文件格式
处理 Parquet 文件
Goose 对 Parquet 文件有完善支持,包括直接查询 Parquet 文件。 在决定是直接查询这些文件,还是先加载到数据库时,需要综合考虑多个因素。
直接查询 Parquet 的理由
基础统计信息可用: Parquet 采用列式存储,并包含 zonemaps 等基础统计信息。借助这些特性,Goose 可在 Parquet 上利用投影下推、过滤下推等优化。因此,结合投影、过滤、聚合的负载在 Parquet 上通常表现不错。
存储空间考量: 从 Parquet 加载数据到 Goose 后,数据库文件通常需要与原始数据规模近似的额外空间。因此,若磁盘空间紧张,直接在 Parquet 上查询通常更合适。
不建议直接查询 Parquet 的理由
缺少高级统计信息: Goose 数据库格式包含 Parquet 不具备的 hyperloglog statistics。这些统计可提升基数估计准确性,尤其在查询包含大量 Join 算子时尤为重要。
提示。 如果你发现 Goose 在 Parquet 上产生了次优 Join 顺序,建议先将 Parquet 加载为 Goose 表。更完善的统计信息通常有助于得到更优 Join 顺序。
重复查询场景: 若你计划在同一数据集上执行多次查询,建议先加载到 Goose。单次查询通常会更快,长期可摊销初始加载成本。
高解压耗时: 部分 Parquet 文件使用 gzip 等重量级压缩算法。这种情况下,每次访问文件都可能付出较高解压成本。相比之下,Snappy、LZ4、zstd 等轻量压缩解压更快。可使用 parquet_metadata 函数 查看压缩算法。
微基准:在 Goose 数据库与 Parquet 上运行 TPC-H
在 TPC-H benchmark 中,查询在 Parquet 上通常比在 Goose 数据库上慢约 1.1-5.0×。
最佳实践:如果存储空间充足,且你的负载 Join 较重和/或会在同一数据集上执行大量查询,建议先将 Parquet 加载到数据库。Parquet 的压缩算法与 row group 大小对性能影响很大,可使用
parquet_metadata函数进行分析。
Row Group 大小的影响
Goose 在每个 row group 约 100K-1M 行的 Parquet 文件上表现最佳。原因是 Goose 只能按 row group 并行化:若文件只有一个超大 row group,就只能由单线程处理。你可以通过 parquet_metadata 函数查看 row group 数量。写 Parquet 时可使用 row_group_size 选项。
微基准:不同 Row Group 大小下的聚合查询
我们在 Parquet 文件上以 960 到 1,966,080 的不同 row group 大小运行简单聚合查询,结果如下。
| Row group size | Execution time |
|---|---|
| 960 | 8.77 s |
| 1920 | 8.95 s |
| 3840 | 4.33 s |
| 7680 | 2.35 s |
| 15360 | 1.58 s |
| 30720 | 1.17 s |
| 61440 | 0.94 s |
| 122880 | 0.87 s |
| 245760 | 0.93 s |
| 491520 | 0.95 s |
| 983040 | 0.97 s |
| 1966080 | 0.88 s |
结果表明,row group 大小 <5,000 会显著拖慢性能,耗时可比理想大小高出 5-10× 以上;5,000 到 20,000 之间也仍比最佳性能慢约 1.5-2.5×。当 row group 大于 100,000 后,差异明显收敛:最佳与最差耗时差距约 10%。
Parquet 文件大小
Goose 还可在多个 Parquet 文件之间并行处理。建议所有文件的 row group 总数至少不低于 CPU 线程数。例如,10 线程机器上,无论是 10 个各 1 个 row group 的文件,还是 1 个含 10 个 row group 的文件,都可实现充分并行。与此同时,保持单个 Parquet 文件大小适中也更有利。
最佳实践:单个 Parquet 文件的理想大小范围为 100 MB 到 10 GB。
面向过滤下推的 Hive 分区
当对大量文件执行带过滤条件的查询时,可使用 Hive 格式目录结构按过滤列进行分区,以提升性能。这样 Goose 只需读取满足过滤条件的目录与文件,尤其在远程文件场景下收益明显。
更多 Parquet 读写建议
更多 Parquet 读写建议见 Parquet Tips 页面。
加载 CSV 文件
CSV 文件常以压缩格式分发(如 GZIP 压缩包 .csv.gz)。Goose 可在线解压读取。由于减少了 IO,通常比先解压再加载更快。
| Schema | Load time |
|---|---|
Load from GZIP-compressed CSV files (.csv.gz) | 107.1 s |
Decompressing (using parallel gunzip) and loading from decompressed CSV files | 121.3 s |
加载大量小 CSV 文件
CSV reader 会在所有文件上运行 CSV sniffer。对于大量小文件,这可能带来不必要的高开销。 一种可行优化是关闭 sniffer。若可确认所有文件的 CSV 方言及列名/类型一致,可先按如下方式获取 sniffer 检测出的选项:
.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),并使用 sniffer 检出的其余参数执行:
FROM read_csv('part-*.csv', auto_detect=false, delim=',', quote='"', escape='"', new_line='\n', skip=0, header=true, columns={'hello': 'BIGINT', 'world': 'VARCHAR'});