Acero 概述#

此页面概述了 Acero 的基本概念,并帮助区分 Acero 与 Arrow 代码库中的其他模块。它适用于用户、开发人员、潜在的贡献者,以及那些希望扩展 Acero 以进行研究或商业用途的人。此页面假设读者已经熟悉核心 Arrow 概念。此页面不要求具备关系代数方面的任何现有知识。

什么是 Acero?#

Acero 是一个 C++ 库,可用于分析大型(可能无限的)数据流。Acero 允许将计算表示为“执行计划”(ExecPlan)。执行计划接收零个或多个输入数据流,并发出单个输出数据流。该计划描述了数据在通过时将如何转换。例如,一个计划可能

  • 使用公共列合并两个数据流

  • 通过针对现有列评估表达式来创建其他列

  • 通过将数据以分区布局写入磁盘来使用数据流

A sample execution plan that joins three streams of data and writes to disk

Acero 不是…#

数据科学家的库#

Acero 并非旨在由数据科学家直接使用。预计最终用户通常会使用某种前端。例如,Pandas、Ibis 或 SQL。Acero 的 API 侧重于功能和可用算法。但是,这些用户可能对更多地了解 Acero 的工作原理感兴趣,以便他们更好地了解其库的后端处理如何运作。

数据库#

数据库(或 DBMS)通常是一个更广泛的应用程序,通常作为独立服务打包。Acero 可能是数据库中的一个组件(大多数数据库都有某种执行引擎),或者可能是某种其他数据处理应用程序中的一个组件,这些应用程序几乎不像数据库。Acero 不关心用户管理、外部通信、隔离、持久性或一致性。此外,Acero 主要关注读取路径,并且写入实用程序缺乏任何类型的事务支持。

优化器#

Acero 没有 SQL 解析器。它没有查询计划器。它没有任何类型的优化器。Acero 希望获得有关如何操作数据的非常详细和低级的指令,然后它将完全按照描述执行该操作。

创建最佳执行计划非常困难。小细节会对性能产生重大影响。我们确实认为优化器很重要,但我们相信它应该独立于 acero 实现,最好通过诸如 Substrait 之类的标准以可组合的方式实现,以便任何后端都可以利用它。

分布式#

Acero 不提供分布式执行。但是,Acero 旨在可由分布式查询执行引擎使用。换句话说,Acero 不会配置和协调工作者,但它确实希望被用作工作者。有时,这种区别有点模糊。例如,Acero 源可能是能够执行过滤或其他高级分析的智能存储设备。有人可能会认为这是一个分布式计划。关键的区别在于 Acero 没有将逻辑计划转换为分布式执行计划的能力。该步骤需要在其他地方完成。

Acero vs…#

Arrow 计算#

这在 与 Arrow C++ 的关系 中有更详细的描述,但关键区别在于 Acero 处理数据流,而 Arrow 计算处理所有数据都在内存中的情况。

Arrow 数据集#

Arrow 数据集库提供了一些基本例程,用于发现、扫描和写入文件集合。数据集模块依赖于 Acero。扫描和写入数据集都使用 Acero。扫描节点和写入节点是数据集模块的一部分。这有助于将文件格式和文件系统的复杂性排除在核心 Acero 逻辑之外。

Substrait#

Substrait 是一个为查询计划建立标准的项目。Acero 执行查询计划并生成数据。这使 Acero 成为 Substrait 消费者。将 Acero 与 Substrait 一起使用 中提供了有关 Substrait 功能的更多详细信息。

Datafusion / DuckDb / Velox / Etc.#

涌现出许多柱状数据引擎。我们认为这是一件好事,并鼓励像 Substrait 这样的项目来帮助允许根据需要在引擎之间切换。我们通常不鼓励进行比较基准测试,因为它们几乎总是受工作负载驱动,并且很少能够捕获 apples-vs-apples 的比较。对每个引擎的优缺点的讨论超出了本指南的范围。

与 Arrow C++ 的关系#

Acero 模块是 Arrow C++ 实现的一部分。它作为一个单独的模块构建,但它依赖于核心 Arrow 模块,并且不能独立存在。Acero 使用并扩展了来自核心 Arrow 模块和 Arrow 计算内核的功能。

A diagram of layers with core on the left, compute in the middle, and acero on the right

核心 Arrow 库为根据 Arrow 柱状格式布局的缓冲区和数组提供容器。除了少数例外,核心 Arrow 库不检查或修改缓冲区的内容。例如,将字符串数组从 lowercase 字符串转换为 uppercase 字符串不会是核心 Arrow 库的一部分,因为这需要检查数组的内容。

计算模块扩展了核心库,并提供了分析和转换数据的函数。计算模块的功能都通过函数注册表公开。Arrow “函数”接受零个或多个数组、批次或表,并生成数组、批次或表。此外,函数调用可以与字段引用和文字结合使用,以形成计算模块可以评估的表达式(函数调用树)。例如,计算 x + (y * 3),给定一个带有列 xy 的表。

A sample expression tree

Acero 通过为数据流添加计算操作来扩展这些功能。例如,project 节点可以将计算表达式应用于一批批流。这将创建一个新的批次流,并将表达式的结果添加为新列。这些节点可以组合成一个图,以形成更复杂的执行计划。这与将函数组合成树以形成复杂表达式的方式非常相似。

A simple plan that uses compute expressions

注意

Acero 不使用核心 Arrow 库中的 arrow::Tablearrow::ChunkedArray 容器。这是因为 Acero 在批次流上运行,因此不需要多批次数据容器。这有助于降低 Acero 的复杂性,并避免了列具有不同块大小的表可能出现的棘手情况。Acero 通常会使用 arrow::Datum,这是核心模块中的一个变体,可以容纳许多不同的类型。在 Acero 中,datum 将始终包含 arrow::Arrayarrow::Scalar

核心概念#

ExecNode#

Acero 中最基本的概念是 ExecNode。ExecNode 具有零个或多个输入以及零个或一个输出。如果 ExecNode 具有零个输入,我们称之为源,如果 ExecNode 没有输出,我们称之为汇。有许多不同类型的节点,每个节点以不同的方式转换其输入。例如

  • 扫描节点是一个从文件读取数据的源节点

  • 聚合节点累积批量数据以计算汇总统计信息

  • 筛选节点根据筛选表达式从数据中删除行

  • 表接收器节点将数据累积到表中

注意

可用计算模块的完整列表包含在用户指南

ExecBatch#

批量数据由 ExecBatch 类表示。ExecBatch 是一个 2D 结构,与 RecordBatch 非常相似。 它可以有零个或多个列,并且所有列必须具有相同的长度。 与 ExecBatch 相比,有几个关键区别

../../_images/rb_vs_eb.svg

记录批处理和执行批处理都对数组和缓冲区具有强大的所有权#

  • ExecBatch 没有 schema。 这是因为假定 ExecBatch 是批量流的一部分,并且假定该流具有一致的 schema。 因此,ExecBatch 的 schema 通常存储在 ExecNode 中。

  • ExecBatch 中的列可以是 ArrayScalar。 当列为 Scalar 时,表示该列对于批处理中的每一行都具有单个值。 ExecBatch 还具有 length 属性,用于描述批处理中有多少行。 因此,查看 Scalar 的另一种方法是具有 length 元素的常量数组。

  • ExecBatch 包含执行计划使用的其他信息。 例如,index 可用于描述批处理在有序流中的位置。 我们期望 ExecBatch 也会演变为包含其他字段,例如选择向量。

../../_images/scalar_vs_array.svg

有四种不同的方式可以使用数组和标量的不同组合来表示给定的批处理数据。所有四个执行批处理应被视为语义等价。#

从记录批处理转换为执行批处理始终是零拷贝。 RecordBatch 和 ExecBatch 都引用完全相同的底层数组。 仅当执行批处理中没有标量时,从执行批处理转换为记录批处理才是零拷贝。

注意

Acero 和计算模块都具有批处理和数组的“轻量级”版本。 在计算模块中,这些被称为 BatchSpanArraySpanBufferSpan。 在 Acero 中,这个概念被称为 KeyColumnArray。 这些类型是同时开发的,并且具有相同的目的。 它们旨在提供一个可以完全堆栈分配的数组容器(前提是数据类型是非嵌套的),以避免堆分配开销。 理想情况下,这两个概念有一天会被合并。

ExecPlan#

ExecPlan 表示 ExecNode 对象的图形。 有效的 ExecPlan 必须始终至少有一个源节点,但从技术上讲不需要有接收器节点。 ExecPlan 包含所有节点共享的资源,并具有控制节点启动和停止执行的实用程序功能。 ExecPlan 和 ExecNode 都与单个执行的生命周期相关联。 它们具有状态,并且预计不可重新启动。

警告

Acero 中的结构,包括 ExecBatch,仍然是实验性的。 ExecBatch 类不应在 Acero 之外使用。 相反,应将 ExecBatch 转换为更标准的结构,例如 RecordBatch

同样,ExecPlan 是一个内部概念。 创建计划的用户应使用 Declaration 对象。 用于使用和执行计划的 API 应该抽象出底层计划的细节,并且不应公开对象本身。

Declaration#

Declaration 是 ExecNode 的蓝图。 可以将 Declaration 组合成一个图形,以形成 ExecPlan 的蓝图。 Declaration 描述了需要完成的计算,但实际上不负责执行计算。 通过这种方式,Declaration 类似于表达式。 预计 Declarations 需要转换为各种查询表示形式(例如 Substrait)以及从各种查询表示形式转换而来。 Declaration 对象是公共 API,与 DeclarationToXyz 方法结合使用,是 Acero 当前的公共 API。

../../_images/decl_vs_ep.svg

Declaration 是用于实例化执行计划实例的蓝图#