ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Apache Arrow C++ 实战:使用 parquet-cpp 的 Arrow 接口读写 Parquet 文件

Apache Arrow C++ 实战:使用 parquet-cpp 的 Arrow 接口读写 Parquet 文件 Apache Arrow C 实战使用 parquet-cpp 的 Arrow 接口读写 Parquet 文件【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow本文是 Apache Arrow C 仓库中 cpp/examples/parquet/parquet_arrow 示例工程的技术解读以该目录下的 README.md 为主线逐行剖析其 CMakeLists.txt 与 reader_writer.cc 源码讲解如何搭建一个消费parquet-cpp库的 CMake 工程以及如何通过parquet/arrow接口完成 Parquet 文件的写入与多种粒度的读取整表、单个 RowGroup、单个列、单个 ColumnChunk。读完本文你将掌握在自有 C 工程中集成 Parquet 读写能力的完整落地方法。示例工程概览一条最小的 Parquet 读写链路该示例工程的目标非常明确展示两件事——如何配置 CMake 以链接parquet-cpp库以及如何使用parquet/arrow接口读写 Apache Parquet 文件。整个示例只包含一个源文件 reader_writer.cc主函数流程如下generate_table()构建一个包含 int64 与 string 两列的 Arrow Table 作为输入数据write_parquet_file()将 Table 以指定 RowGroup 大小写入 Parquet 文件read_whole_file()一次性读回整个文件read_single_rowgroup()只读取第 0 个 RowGroupread_single_column()只读取整文件的第 0 列read_single_column_chunk()只读取第 0 个 RowGroup 中的第 0 个 ColumnChunk。这六步覆盖了parquet/arrow接口中最常用的写一条、读四种典型场景也是实际业务中最常见的读写粒度需求。CMake 工程搭建从零链接 parquet-cpp工程根目录的 CMakeLists.txt 展示了独立工程消费 parquet-cpp 的标准写法# Require cmake that supports BYPRODUCTS in add_custom_command, ExternalProject_Add [1]. cmake_minimum_required(VERSION 3.25) project(parquet_arrow_example) include(ExternalProject) include(FindPkgConfig) include(GNUInstallDirs) option(PARQUET_LINK_SHARED Link to the Parquet shared library ON) # This ensures that things like -stdgnu... get passed correctly if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 20) endif() # We require a C20 compliant compiler set(CMAKE_CXX_STANDARD_REQUIRED ON) # Look for installed packages the system find_package(Parquet REQUIRED) add_executable(parquet-arrow-example reader_writer.cc) if(PARQUET_LINK_SHARED) target_link_libraries(parquet-arrow-example Parquet::parquet_shared) else() target_link_libraries(parquet-arrow-example Parquet::parquet_static) endif()其中几个关键点值得展开说明CMake 最低版本 3.25注释中明确说明要求该版本是为了支持add_custom_command/ExternalProject_Add中的BYPRODUCTS特性C20 强制要求工程默认将CMAKE_CXX_STANDARD设为 20并通过CMAKE_CXX_STANDARD_REQUIRED ON强制编译器支持 C20这是 Arrow 与 Parquet C 库当前构建的前提find_package(Parquet REQUIRED)要求系统中已安装并导出 CMake 包配置的 parquet-cpp 库安装后即可通过Parquet::parquet_shared共享库或Parquet::parquet_static静态库这两个导入目标进行链接链接方式可切换通过PARQUET_LINK_SHARED选项默认 ON在共享/静态链接之间切换方便在不同部署场景下复用。值得一提的是该示例也被集成进了仓库整体的 Parquet 示例构建体系中父目录 cpp/examples/parquet/CMakeLists.txt 中通过add_executable(parquet-arrow-example parquet_arrow/reader_writer.cc)将其与 low_level_api、parquet_stream_api 等示例一并编译并优先使用共享库、在共享构建被禁用时回退到静态库可对照参考。构建输入数据用 Arrow Builder 构造 Table示例首先用 Arrow 的 Builder API 构造内存数据。arrow::Int64Builder与arrow::StringBuilder分别对应两列std::shared_ptrarrow::Table generate_table() { arrow::Int64Builder i64builder; PARQUET_THROW_NOT_OK(i64builder.AppendValues({1, 2, 3, 4, 5})); std::shared_ptrarrow::Array i64array; PARQUET_THROW_NOT_OK(i64builder.Finish(i64array)); arrow::StringBuilder strbuilder; PARQUET_THROW_NOT_OK(strbuilder.Append(some)); PARQUET_THROW_NOT_OK(strbuilder.Append(string)); PARQUET_THROW_NOT_OK(strbuilder.Append(content)); PARQUET_THROW_NOT_OK(strbuilder.Append(in)); PARQUET_THROW_NOT_OK(strbuilder.Append(rows)); std::shared_ptrarrow::Array strarray; PARQUET_THROW_NOT_OK(strbuilder.Finish(strarray)); std::shared_ptrarrow::Schema schema arrow::schema( {arrow::field(int, arrow::int64()), arrow::field(str, arrow::utf8())}); return arrow::Table::Make(schema, {i64array, strarray}); }第一列intint64追加了 5 个整数 1~5第二列strutf8 字符串逐条追加 5 个字符串some/string/content/in/rows最终通过arrow::schema()与arrow::Table::Make()将两列拼成一个 5 行 2 列的 Table。这里的PARQUET_THROW_NOT_OK宏来自 cpp/src/parquet/exception.h它会将arrow::Status错误转换为parquet::ParquetStatusException并抛出对应的PARQUET_ASSIGN_OR_THROW则用于解包arrow::ResultT失败时同样抛异常。它们让示例代码无需逐处判断 Status保持主线清晰。写入 ParquetWriteTable 与 RowGroup 大小写入逻辑在write_parquet_file()中完成void write_parquet_file(const arrow::Table table) { std::shared_ptrarrow::io::FileOutputStream outfile; PARQUET_ASSIGN_OR_THROW( outfile, arrow::io::FileOutputStream::Open(parquet-arrow-example.parquet)); // The last argument to the function call is the size of the RowGroup in // the parquet file. Normally you would choose this to be rather large but // for the example, we use a small value to have multiple RowGroups. PARQUET_THROW_NOT_OK( parquet::arrow::WriteTable(table, arrow::default_memory_pool(), outfile, 3)); }输出流通过arrow::io::FileOutputStream::Open()打开目标文件路径返回 Arrow 的 IO 抽象核心写入口parquet::arrow::WriteTable(table, pool, sink, chunk_size)其声明位于 cpp/src/parquet/arrow/writer.h函数签名完整形态为::arrow::Status WriteTable(const ::arrow::Table table, MemoryPool* pool, std::shared_ptr::arrow::io::OutputStream sink, int64_t chunk_size DEFAULT_MAX_ROW_GROUP_LENGTH, std::shared_ptrWriterProperties properties default_writer_properties(), std::shared_ptrArrowWriterProperties arrow_properties default_arrow_writer_properties());最后一个参数是 RowGroup 大小示例传入3表示每个 RowGroup 最多 3 行。数据共 5 行因此会切分出多个 RowGroup——这正是源码注释强调的点真实场景中 RowGroup 通常要选得很大以提升压缩与查询效率此处刻意用小值以便演示按 RowGroup 读取。不传该参数时默认使用DEFAULT_MAX_ROW_GROUP_LENGTH源码中为 64 * 1024 * 1024即 64 Mi 行如需更精细的控制可改用具名的 FileWriter 类逐 RowGroup、逐 ColumnChunk 迭代写入并通过WriterProperties/ArrowWriterProperties配置压缩、编码、use_threads等选项。读取 Parquet四种读取粒度逐一解析方式一整表一次读入std::unique_ptrparquet::arrow::FileReader reader; PARQUET_ASSIGN_OR_THROW(reader, parquet::arrow::OpenFile(infile, arrow::default_memory_pool())); std::shared_ptrarrow::Table table; PARQUET_ASSIGN_OR_THROW(table, reader-ReadTable()); std::cout Loaded table-num_rows() rows in table-num_columns() columns. std::endl;parquet::arrow::OpenFile(RandomAccessFile, MemoryPool)是最简单的 FileReader 工厂函数其声明位于 cpp/src/parquet/arrow/reader.h。文件头注释对读取接口的定位描述得很清楚在最简单的形态下我们满足想一次性读取整个 Parquet 文件的用户即FileReader::ReadTable()。ReadTable()返回包含全部行列的arrow::Table适合数据量可控、需要整体载入内存分析的场景。若需要更高级的设置如线程数、批大小、列投影应使用FileReaderBuilder或FileReader上的set_use_threads()/set_batch_size()等方法。方式二只读单个 RowGroupstd::shared_ptrarrow::Table table; PARQUET_THROW_NOT_OK(reader-RowGroup(0)-ReadTable(table));RowGroup(i)返回RowGroupReader其ReadTable()只物化指定 RowGroup 的行。reader.h 的注释指出想在单个 Parquet 文件之上实现并行处理的进阶用户应该在 RowGroup 粒度上做并行——即多个线程各自RowGroup(i)-ReadTable()天然形成数据分片这是分布式与多线程读取的推荐切分单位。方式三只读整文件的单个列std::shared_ptrarrow::ChunkedArray array; PARQUET_THROW_NOT_OK(reader-ReadColumn(0, array)); PARQUET_THROW_NOT_OK(arrow::PrettyPrint(*array, 4, std::cout));FileReader::ReadColumn(i)将第 i 个顶层 schema 字段对应的整列跨所有 RowGroup读为arrow::ChunkedArray并用arrow::PrettyPrint打印。注意列索引语义它指向顶层 schema 字段可能是嵌套 struct例如嵌套foo.bar中i0会读整个foo结构体。方式四只读某个 RowGroup 内的单个 ColumnChunkstd::shared_ptrarrow::ChunkedArray array; PARQUET_THROW_NOT_OK(reader-RowGroup(0)-Column(0)-Read(array)); PARQUET_THROW_NOT_OK(arrow::PrettyPrint(*array, 4, std::cout));RowGroupReader::Column(j)返回ColumnChunkReader其Read()读取的是 Parquet 文件中第 i 个 RowGroup × 第 j 列这一最小存储单元ColumnChunk。reader.h 注释将其描述为最进阶的用法消费者可以并行读取不同 RowGroup并在每个 RowGroup 内逐列独立消费从而最大化 I/O 与 CPU 并行度。编译运行与预期输出在已安装 parquet-cpp并导出 CMake 配置的环境中可独立构建该示例mkdir build cd build cmake .. -DPARQUET_LINK_SHAREDON cmake --build . ./parquet-arrow-example程序运行后在当前目录生成parquet-arrow-example.parquet随后依次输出四段读取日志Reading parquet-arrow-example.parquet at once→Loaded 5 rows in 2 columns.Reading first RowGroup of parquet-arrow-example.parquet→Loaded 3 rows in 2 columns.RowGroup 大小为 3Reading first column of parquet-arrow-example.parquet→ 打印 int 列[1, 2, 3, 4, 5]Reading first ColumnChunk of the first RowGroup of ...→ 打印第一个 ColumnChunk 的列数据需要说明的是示例在仓库中作为 Parquet 示例集的一部分随主构建编译见 cpp/examples/parquet/CMakeLists.txt其可执行目标名为parquet-arrow-example如果你只是阅读代码也可直接以源码 reader_writer.cc 为模板替换为自己的数据与文件路径。从示例到生产可扩展的方向从源码结构看示例刻意保持了最小化但它演示的接口背后还留有多层进阶空间供生产环境按需扩展读取侧FileReader::GetRecordBatchReader()可返回 RecordBatch 流支持按行组/列子集投影并在ArrowReaderProperties开启use_threads时对列解码进行并行化GetRecordBatchGenerator()则提供异步生成器形态写入侧FileWriter支持NewRowGroup()/WriteColumnChunk()逐块迭代写入NewBufferedRowGroup()配合WriteRecordBatch()实现缓冲行组批量落盘WriterProperties可配置压缩算法、编码方式、页大小等元数据互通Parquet 的field_id会映射为 Arrow schema 字段上的PARQUET:field_id元数据键保证跨语言 schema 对齐。小结cpp/examples/parquet/parquet_arrow 示例以极少的代码完整演示了CMake 链接 parquet-cpp Arrow 接口读写 Parquet的闭环写入侧一条WriteTable即可按 RowGroup 切分落盘读取侧从整表、单 RowGroup、单列到单 ColumnChunk 逐级细化对应了从简单加载到细粒度并行消费的完整需求谱系。理解这六步之后你可以把相同的模式直接迁移到自己的数据处理管线中。【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表