Apache Arrow C++ Tabular Data 指南:Field、Schema、Table 与 RecordBatch 全解析

Apache Arrow C++ Tabular Data 指南:Field、Schema、Table 与 RecordBatch 全解析 Apache Arrow C Tabular Data 指南Field、Schema、Table 与 RecordBatch 全解析【免费下载链接】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 官方文档 docs/source/cpp/tables.rst 为核心系统讲解二维异构数据在 Arrow 中的四种核心抽象——arrow::Field、arrow::Schema、arrow::Table与arrow::RecordBatch。读完本文你将掌握用工厂函数创建字段与 schema、用 chunked array 组织表、用 record batch 做增量序列化以及在 Table 与 RecordBatch 之间进行零拷贝转换TableBatchReader/Table::FromRecordBatches的完整实战方案。为什么需要表格抽象从一维数组到二维数据集在 Arrow 中Array与ChunkedArray表示一维的、同质取值的序列例如一列 int32或一列 utf8 字符串。但在真实数据处理中数据往往以二维的、异构的形式出现——数据库表、CSV 文件、Parquet 文件都是典型例子同一张表的不同列可能拥有完全不同的数据类型。为此Arrow 提供了一组自下而上的抽象来高效、便捷地处理这类数据自底向上依次为arrow::Field描述某一列——列名 数据类型 元数据arrow::Schema描述整张表——有序的字段序列 schema 级元数据arrow::Table内存中的二维数据集每列是一个ChunkedArrayarrow::RecordBatch等长数组构成的二维数据集是 IPC 序列化与流式计算的基本工作单元。下面按依赖顺序逐一深入。Field列的身份证Field用于标识一张表的特定列同时也用于标识嵌套数据类型如arrow::StructType的特定成员。一个arrow::Field实例聚合了三样信息数据类型data type该列取值的类型字段名field name列的标识可选元数据optional metadata键值对形式的附加信息。从 type_fwd.h 中的工厂函数声明可以看到其完整签名std::shared_ptrField field(std::string name, std::shared_ptrDataType type, bool nullable true, std::shared_ptrconst KeyValueMetadata metadata NULLPTR); std::shared_ptrField field(std::string name, std::shared_ptrDataType type, std::shared_ptrconst KeyValueMetadata metadata);其中nullable默认值为true即字段默认允许空值metadata默认缺省还有一个重载允许只传(name, type, metadata)此时字段被假定为可空。官方文档明确推荐创建 Field 的方式是调用arrow::field工厂函数而不是直接实例化类。Schema二维数据集的整体蓝图Schema描述二维数据集如表的整体结构它保存一个有序的字段序列外加可选的schema 级元数据与每个字段各自携带的元数据相互独立。推荐的创建方式同样是调用工厂函数arrow::schema。官方文档 tables.rst 给出的最小示例// 创建一个描述两列数据集的 schema // 一个 int32 列 A 和一个 utf8 编码的字符串列 B std::shared_ptrarrow::Field field_a, field_b; std::shared_ptrarrow::Schema schema; field_a arrow::field(A, arrow::int32()); field_b arrow::field(B, arrow::utf8()); schema arrow::schema({field_a, field_b});从源码 type_fwd.h 可以印证工厂函数的底层形态std::shared_ptrSchema schema(FieldVector fields, std::shared_ptrconst KeyValueMetadata metadata NULLPTR);工厂函数接收一个FieldVector字段的 vector以及可选的 schema 级元数据此外还有一个从(name, type)二元组序列构造 schema 的重载此时所有字段默认可空且不带元数据。schema()返回shared_ptrSchema可以直接传给Table::Make、RecordBatch::Make以及后续的序列化接口。Table以 ChunkedArray 为列的二维数据集arrow::Table是一个二维数据集特点是每列是一个ChunkedArray即分块的数组一个逻辑列由若干物理数组片段拼接而成伴随一个 schema 提供字段名每个 chunked 列必须具有相同的逻辑长度元素个数但各列可以以不同的方式进行分块chunking。关于不同列可以不同分块这一点官方文档 tables.rst 特别强调而源码 table.h 中的注释也印证了Table的本质是 Logical table as sequence of chunked arrays逻辑表 chunked array 的序列。Table 的常见构造方式从 table.h 可以看到Table提供多组静态工厂方法Table::Make(schema, columns, num_rows -1)从 schema vectorshared_ptrChunkedArray构造num_rows缺省为 -1 表示从各列自动推断行数若列数为 0则表的行数为 0Table::Make(schema, arrays, num_rows -1)从 schema vectorshared_ptrArray构造每个 Array 自动包装成单 chunk 的 ChunkedArrayTable::MakeEmpty(schema, pool)创建空表每列生成一个空 chunkTable::FromRecordBatchReader(reader)从RecordBatchReader累积构造Table::FromRecordBatches(batches)与Table::FromRecordBatches(schema, batches)从记录批序列构造见下文Table::FromChunkedStructArray(array)从 chunked StructArray 构造每个 struct 字段展开为一列。常用访问与变换接口Table还提供了丰富的列操作接口全部返回新表而不修改原表函数式风格schema()/column(i)/columns()/field(i)/fields()读取元信息与列数据GetColumnByName(name)按列名取列找不到返回nullptrSlice(offset, length)零拷贝地取表的行切片table.hAddColumn/RemoveColumn/SetColumn/SelectColumns/RenameColumns增删改列与列重命名CombineChunks()把每列的所有 chunk 拼接为 0 或 1 个 chunk二进制列可能因缓冲区上限而保留多个 chunkCombineChunksToBatch()则把整张表合并为一个RecordBatchtable.hValidate()轻量检查复杂度 O(字段数 × chunk 数)与ValidateFull()深度检查O(字段数 × 行数)ToTensor(null_to_nan, row_major, pool)将表转换为Tensor。这些接口在 table_test.cc 等测试中都有覆盖验证例如FromRecordBatches测试确认空批次列表会报Invalidschema 不一致的批次混用也会报Invalid。RecordBatch等长连续数组构成的二维数据集arrow::RecordBatch是另一种二维数据集抽象其约束比 Table 更严格它由**若干连续的、等长的数组Array**构成与 Table 一样带有 schema且 schema 必须与各数组的数据类型匹配。由于每个列都是单一连续数组不存在内部 chunk 边界RecordBatch 天然适合作为各种序列化与计算函数的工作单元并且支持增量式处理——一批一批地读取、传输或计算。这一点在官方文档 tables.rst 中被明确为 RecordBatch 的核心价值。RecordBatch 的构造record_batch.h 提供的主要工厂方法static std::shared_ptrRecordBatch Make( std::shared_ptrSchema schema, int64_t num_rows, std::vectorstd::shared_ptrArray columns, std::shared_ptrDevice::SyncEvent sync_event NULLPTR); static Resultstd::shared_ptrRecordBatch MakeEmpty( std::shared_ptrSchema schema, MemoryPool* pool default_memory_pool());其中num_rows必须与每个数组的长度一致MakeEmpty会按 schema 生成各类型对应的空数组。此外还有FromStructArray/ToStructArray与 struct array 互转通常零拷贝、Slice零拷贝行切片、AddColumn/SetColumn/RemoveColumn/SelectColumns/RenameColumns等与 Table 对偶的接口。可移植性差异Table 是 C 实现概念RecordBatch 才是格式概念这是一个非常关键的设计区别官方文档 tables.rst 专门强调RecordBatch 可以通过 IPC见 format/IPC.rst 与 Columnar 格式规范中的format-ipc锚点见 docs/source/format/Columnar.rst或 C Data Interface见 docs/source/format/CDataInterface.rst在实现之间直接传输Table 和 ChunkedArray 是 C 实现中的概念并不存在于 Arrow 格式规范本身因此它们不能直接跨实现移植。换句话说面向格式边界进程间、语言间、文件间时用 RecordBatch面向 C 内存内分析时用 Table。二者之间的转换则是零拷贝的。零拷贝互转TableBatchReader 与 Table::FromRecordBatches官方文档 tables.rst 指出Table 与 RecordBatch 之间的相互转换无需复制底层数组缓冲区Table → 一批 RecordBatch使用arrow::TableBatchReader将一张表流式切分成任意数量的 record batchRecordBatch 序列 → Table使用arrow::Table::FromRecordBatches工厂函数把逻辑上连续的批次组装成表。TableBatchReader 的实现机制TableBatchReader继承自RecordBatchReadertable.h头文件注释明确The conversion is zero-copy: each record batch is a view over a slice of the tables columns.转换是零拷贝的每个 record batch 都是表列某个切片的视图。从实现 table.cc 可以看到其内部工作原理构造时保存各列的ChunkedArray*并记录每个列当前所处的 chunk 序号与 chunk 内偏移set_chunksize(int64_t chunksize)设置期望的批次最大行数内部存为max_chunksize_默认是std::numeric_limitsint64_t::max()即默认不限制批次大小ReadNext逐次计算所有列公共的连续切片取min(剩余总行数, max_chunksize_)与各列当前 chunk 剩余长度的最小值切出一个视图批次读完全部行后返回nullptr表示流结束。因此实际每个批次的行数可能小于设定的chunksize这取决于各列的 chunk 边界——这正是set_chunksize的注释所说明的行为The actual number of rows in each record batch may be smaller, depending on actual chunking characteristics of each table column。使用方式示例std::shared_ptrarrow::Table table /* 已有的一张表 */; arrow::TableBatchReader reader(table); reader.set_chunksize(1024); // 每批最多 1024 行 std::shared_ptrarrow::RecordBatch batch; while (reader.ReadNext(batch).ok() batch ! nullptr) { // 对每个批次做序列化、网络传输或增量计算 }Table::FromRecordBatches 的两种重载反向组装由Table::FromRecordBatches完成table.h// 重载 1schema 取自第一个 RecordBatch static Resultstd::shared_ptrTable FromRecordBatches( const std::vectorstd::shared_ptrRecordBatch batches); // 重载 2显式传入 schema允许零个批次此时生成空表 static Resultstd::shared_ptrTable FromRecordBatches( std::shared_ptrSchema schema, const std::vectorstd::shared_ptrRecordBatch batches);测试 table_test.cc 验证了其行为边界两个 schema 一致的批次合并后各列形成包含两个 chunk 的ChunkedArray结果与Table::Make直接构造等价空的批次列表调用重载 1 会报Invalid无法推断 schemaschema 不一致的批次混用会报Invalid重载 2 传空批次列表则合法返回 0 行的空表对应FromRecordBatchesZeroLength测试见 table_test.cc。值得注意的是CombineChunks测试table_test.cc展示了两者配合的典型场景把两批数据FromRecordBatches组装成表后各列 chunk 数为 2再经CombineChunks()压缩后每列变为 1 个 chunk。表级聚合与 schema 统一ConcatenateTables 与 PromoteTableToSchema除了单表操作table.h 还提供了两个表级工具函数它们也是 docs/source/cpp/api/table.rst 中收录的 Table 配套 APIConcatenateTables(tables, options, memory_pool)按传入顺序把多张表纵向拼接成一张新表行序保持不变。schema 完全一致时新表直接从既有列 chunk 组装不复制数据若 schema 不完全一致可通过ConcatenateTablesOptions开启unify_schemas默认false先统一 schema再转换为统一类型此时可能产生拷贝field_merge_options控制统一 schema 时同名 field 的合并策略。PromoteTableToSchema(table, schema, ...)把表提升到目标 schema目标 schema 中有、表中没有的字段 → 补一列 null表中为 Null 类型的列 → 按目标类型填充 null 值类型不兼容、表中存在 schema 之外的列、或无法完成 cast 时返回错误可选传入compute::CastOptions控制类型转换行为。这些工具与 Table 的Equals含ApproxEquals近似比较一起构成了 Table 层数据整理的完整工具箱。总结如何选择 Table 与 RecordBatch维度arrow::Tablearrow::RecordBatch列的数据结构ChunkedArray可分块连续Array等长、无内部边界行数约束各列逻辑长度相同分块方式可不同各列物理长度严格相同是否属于 Arrow 格式规范否C 实现概念是可经 IPC / C Data Interface 传输典型用途内存内列式分析、大数据集承载增量序列化、流式计算、跨语言互操作互转方式TableBatchReader流出批次Table::FromRecordBatches组装成表围绕官方文档 tables.rst 的核心脉络本文从arrow::field/arrow::schema两个工厂函数出发逐一拆解了Table与RecordBatch的结构约束、构造方法、常用接口与可移植性差异并结合 table.h、record_batch.h、table.cc 与 table_test.cc 中的实现与测试验证了零拷贝互转的底层机制及边界条件。掌握这套抽象后你可以在内存分析Table与格式传输RecordBatch两种场景间自由切换既保留列式分析的高效性又获得跨实现互操作的能力。【免费下载链接】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),仅供参考