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 与…#

Arrow 计算#

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

Arrow 数据集#

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

Substrait#

Substrait 是一个为查询计划建立标准的项目。Acero 执行查询计划并生成数据。这使得 Acero 成为 Substrait 的使用者。将 Acero 与 Substrait 结合使用中更详细地介绍了 Substrait 的功能。

Datafusion / DuckDb / Velox / 等#

目前正在涌现许多列式数据引擎。我们认为这是一件好事,并鼓励 Substrait 等项目,以帮助根据需要在引擎之间切换。我们通常不鼓励进行比较基准测试,因为它们几乎不可避免地会受到工作负载的驱动,并且很少能够捕捉到苹果与苹果之间的比较。讨论每种方法的优缺点超出了本指南的范围。

与 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 核心库不会检查或修改缓冲区的内容。例如,将字符串数组从小写字符串转换为大写字符串不属于 Arrow 核心库的一部分,因为这需要检查数组的内容。

计算模块扩展了核心库,并提供了分析和转换数据的函数。计算模块的功能都通过函数注册表公开。Arrow“函数”接受零个或多个数组、批次或表格,并生成一个数组、批次或表格。此外,函数调用可以与字段引用和字面量组合起来形成表达式(函数调用的树),计算模块可以对其进行求值。例如,给定一个包含列 xy 的表格,计算 x + (y * 3)

A sample expression tree

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

A simple plan that uses compute expressions

注意

Acero 不使用核心 Arrow 库中的 arrow::Tablearrow::ChunkedArray 容器。这是因为 Acero 对批处理流进行操作,因此不需要多批处理数据容器。这有助于降低 Acero 的复杂性,并避免因表中列具有不同块大小而引起的棘手情况。Acero 通常会使用 arrow::Datum,它是核心模块中的一个变体,可以容纳许多不同的类型。在 Acero 中,一个 Datum 将始终包含一个 arrow::Array 或一个 arrow::Scalar

核心概念#

执行节点(ExecNode)#

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

  • 扫描节点是一个源节点,它从文件中读取数据。

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

  • 过滤节点根据过滤表达式从数据中删除行。

  • 表汇聚节点将数据累积到一个表中。

注意

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

执行批次(ExecBatch)#

数据批次由 ExecBatch 类表示。ExecBatch 是一个与 RecordBatch 非常相似的二维结构。它可以有零个或多个列,并且所有列必须具有相同的长度。ExecBatch 与 RecordBatch 之间有一些关键区别:

../../_images/rb_vs_eb.svg

记录批次和执行批次都对数组和缓冲区拥有强所有权#

  • ExecBatch 没有模式。这是因为假定 ExecBatch 是批处理流的一部分,并且假定该流具有一致的模式。因此,ExecBatch 的模式通常存储在 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 是一个内部概念。创建计划的用户应该使用声明对象。用于使用和执行计划的 API 应该抽象化底层计划的细节,而不是公开对象本身。

声明(Declaration)#

声明是 ExecNode 的蓝图。声明可以组合成一个图来形成 ExecPlan 的蓝图。声明描述了需要完成的计算,但实际上并不负责执行计算。在这种情况下,声明类似于表达式。预计声明需要与各种查询表示形式(例如 Substrait)相互转换。声明对象是公共 API,与 DeclarationToXyz 方法一起,是 Acero 当前的公共 API。

../../_images/decl_vs_ep.svg

声明是一个蓝图,用于实例化执行计划实例#