c++如何解析Apache Parquet格式的分栏存储数据【深度】

陌涛大大_6019

陌涛大大_6019

2026-04-12

675人浏览

原创

最靠谱方案是使用 Apache Arrow C++ 库的 parquet::ParquetFileReader,它自动校验 magic number、解析 Thrift 元数据、支持多压缩算法及谓词下推,避免手动解析的复杂性与错误。

c++如何解析apache parquet格式的分栏存储数据【深度】

用 Apache Arrow C++ 库读 Parquet 文件最靠谱

直接手写 Parquet 解析器不现实——格式涉及页压缩、字典编码、列元数据嵌套、页头校验、thrift 元数据解析等,C++ 生态里唯一成熟、被广泛验证的选择是 apache-arrow 的 C++ 实现。它封装了全部底层细节,提供 parquet::ParquetFileReader 和 Arrow 表抽象,是当前事实标准。

常见错误是试图用旧版 parquet-cpp(已归档)或自己解析 thrift schema buffer,结果卡在 Invalid parquet file: missing magic number 或解不出重复/定义层级。Arrow 的 ParquetFileReader::Open() 会自动校验 magic bytes 并加载元数据,省掉所有手动跳过 footer、反序列化 FileMetaData 的麻烦。

实操建议:

  • 用 v14.0.2+ 版本(支持 INT96 时间戳、LZ4_FRAME、delta encoding 等新特性)
  • 链接时确保同时带 -larrow -lparquet -lzstd -llz4 -lbrotli,缺任意一个压缩库都可能触发 IOError: Unsupported compression: LZ4
  • 若读加密 Parquet(如 KMS 封装),需额外启用 ARROW_WITH_OPENTELEMETRY=OFF 并自定义 fs::FileSystem,默认不支持

读取时控制列裁剪与类型映射要靠 parquet::ArrowReaderProperties

Parquet 是列存,但默认 FileReader::ReadTable() 会把所有列转成 Arrow Table,浪费内存和 CPU。真正高效的用法是显式指定需要的列名,并控制 Arrow 类型映射逻辑。

例如原始 Parquet 中某列为 INT32 + PLAIN_DICTIONARY 编码,Arrow 默认映射为 dictionary;但如果你后续要传给 Eigen 或做数值计算,更想要展开后的 int32 数组,就得关掉字典自动提升:

auto props = parquet::ArrowReaderProperties::Defaults();
props.set_use_threads(true); // 启用多线程解码
props.set_pre_buffer(true);  // 预分配解码 buffer,减少 realloc
props.set_read_dictionary({{"user_id", false}}); // 强制 user_id 不走 dictionary 展开

其他关键参数:

  • set_batch_size(65536):影响每次从页读取的 record batch 大小,太小导致频繁虚函数调用,太大易 OOM
  • set_use_threads(false):某些嵌入式场景需禁用线程池,否则 std::thread 构造失败会 crash
  • set_coerce_int96_timestamp_unit(TimeUnit::MILLI):避免 INT96 时间戳转成纳秒级 TimestampArray 后溢出

parquet::Scanner 比 FileReader 更适合大文件过滤下推

当 Parquet 文件超过 1GB,且你只想要满足 WHERE ts > '2023-01-01' AND status = 1 的行时,用 FileReader::ReadTable() 先全量加载再 filter 是灾难性的。Arrow 15.0+ 引入的 parquet::Scanner 支持谓词下推(predicate pushdown),能跳过不满足条件的 RowGroup 和页。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

核心步骤是构造 Expression 并传入 ScanOptions:

auto expr = and_(greater_than(field_ref("ts"), scalar(timestamp_ms(1672531200000))),
                 equal(field_ref("status"), scalar(int32_t(1))));
auto scan_options = std::make_shared<:scanoptions>();
scan_options->use_threads = true;
scan_options->batch_size = 8192;
auto scanner = parquet::Scanner::Make(reader, scan_options).ValueOrDie();
scanner->UseAsync(true); // 启用异步 IO(需编译时开启 ARROW_WITH_THREADS)
auto table = scanner->ToTable(expr).ValueOrDie(); // 谓词在此处下推
</:scanoptions>

注意点:

  • 仅支持 EQ/GT/LT/AND/OR 等基础表达式,不支持 LIKE 或正则
  • RowGroup 级过滤依赖 Parquet 文件的统计信息(min/max),如果写入时没写统计(如 Spark 3.0+ 默认关了 parquet.enable.dictionary),下推会失效
  • UseAsync(true) 在 Windows 上需链接 ws2_32.lib,否则运行时报 undefined symbol: WSAGetLastError

写 Parquet 时别忽略 WriterProperties 对压缩与页大小的实际影响

很多人用 parquet::ParquetWriter::Open() 写完发现文件比预期大 3 倍,或者 Spark 读报 Unsupported encoding: DELTA_BINARY_PACKED,问题几乎都出在 WriterProperties 配置上。

默认配置对通用性做了妥协:页大小 1MB、压缩用 SNAPPY、不启用字典、不写统计。生产环境应按数据特征调优:

  • 高基数字符串列(如 UUID):设 enable_dictionary = true + dictionary_pagesize_limit = 1024 * 1024,能压到 1/5 体积
  • 时间序列整数:开 enable_statistics = true + data_page_version = ParquetDataPageVersion::V2,V2 支持 DELTA 编码,比 PLAIN 快 2–3 倍
  • 写吞吐敏感场景:增大 data_pagesize = 4 * 1024 * 1024 减少页头开销,但会提高延迟
  • 跨引擎兼容:禁用 compression = Compression::UNCOMPRESSED 或强制 compression = Compression::SNAPPY,避免 ZSTD 导致 Trino 报错

最后提醒:Arrow 的 Table 写 Parquet 时,timestamp(us) 列默认按 INT96 存,而 Spark 3.4 默认读 INT64;要么写时加 use_deprecated_int96_timestamps = false,要么读时统一用 Arrow 的 timestamp_us() 类型声明。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

apache c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1938

5

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

3224

5

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6404

49

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2023.11.24

6609

6

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

2024.03.22

8134

16

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2024.04.29

12674

11

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

2025.07.29

6579

17

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

2025.08.07

7407

13

常用字符串方法大全
常用字符串方法大全

常用字符串方法大全

2025.08.08

3628

14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Apache Maven 官方安装指南
Apache Maven 官方安装指南

共0课时 | 0人学习

Apache Maven 官方用户中心
Apache Maven 官方用户中心

共0课时 | 0人学习

Apache Subversion 官方手册
Apache Subversion 官方手册

共0课时 | 0人学习