oss

Apache Arrow 的真正产品是一份内存约定:buffers、record batches 与 zero-copy 的成立条件

7 条来源 5 条一手来源 已翻译 2026年4月2号

正文
Wes McKinney 在会议舞台上发言,身穿深色上衣,佩戴头戴式麦克风,背景是明亮的会场屏幕。

这张会议照片适合本文,因为 Apache Arrow 到今天仍然像一个维护者意图非常清楚的架构主张:它把分析系统推向同一种列式内存布局与跨进程交接方式。[7]

Apache Arrow 常被介绍成一个很快的列式库。这个说法成立,但只说到了一小半。Arrow 真正交给数据系统的,是一份内存约定:数组怎样表示,空值放在哪里,偏移量怎样记录,批次怎样切分,流又怎样传走。有了这套共同说法,一个运行时生成的表,交到另一个运行时手里时,就少了一轮逐行拆开再装回去的苦工。[1][2][3][4]

把 Arrow 放到 2026 年再看,最值得抓住的是这条线索。若只把它看成“又一个数据框架”,项目清单会很散:格式规范、IPC 消息、Flight RPC、C 接口、多语言绑定、计算内核,再加上一大片相邻工具。[1][2][3][4] 换成“共同交接点”来读,这些部件就连到了一起。多套引擎可以先同意同一种列式摆放方式和同一种交付方式,再把各自的差异留给查询、优化、存储或语言绑定去处理。

配图说明:题图使用 Wikimedia Commons 上 Wes McKinney 的真实会议照片。它适合本文,因为文章讨论的是一种到今天仍然带着创始人意图的架构选择:把分析型数据的内存布局标准化,让不同系统在互操作时沿着同一条边界继续合作。[7]

1. Arrow 从 table 之下的 buffer 开始

Arrow 列式格式最硬的一点,在于它把起点压得很低,低过 SQL 表和 dataframe API,直接来到物理数组:连续 buffer、明确的空值表示,以及变长值的固定写法。[2] 规范写得很直白,数组通常带着自己的 validity bitmap;变长值用 offsets buffer 加 data buffer 表示。[2] Arrow 能在分析引擎之间流动,第一层原因就在这里。它先规定字节怎样排放,再让上层系统决定这些数据叫表、列、Series 还是别的名字。

这种排放纪律,是 Arrow 能被多方接受的第一步。一个定宽基础类型列,对应 values buffer 和有效性状态。[2] 一个字符串列,对应 offsets 和 data,也带着空值位图。[2] 嵌套数组继续沿着同一套规则往里展开。系统先在这层物理语法上达成一致,分析数据在它们之间移动时,重塑形状的代价就会降下来。

规范里关于对齐的要求,把这层意图写得更清楚。Arrow 推荐对内存做对齐分配,并建议在合适时采用 64-byte 对齐和 padding,以便实现方更容易配合向量化执行和 CPU cache 行为。[2] Arrow 的速度声誉,扎实处也在这里。上层引擎能够信任 buffer 的排放方式,才可以用紧凑循环连续读取大量值,省下额外重组。

record batch 是下一块关键单元。Arrow 给出的单位,是 schema 加上一组有范围的列式数组;这些 batch 可以被流式传输、交给另一个库,也可以再组合起来。[2] 单位切成 batch 后,Arrow 在单进程内部和跨传输交付时都能保持同一种手感。系统每次递过去的是一块已经能工作的分析数据,免去临时拼装行式协议的工夫。

2. C Data Interface 把格式规范带到真实交接里

只靠格式规范还不够。很多项目都写过自己的存储格式或序列化规范,最后没有变成日常互操作。Arrow 的 C Data Interface,解决的是工程现场真正会碰到的交接问题。

它的 rationale 写得很坦白:不少项目想交换 Arrow 兼容数据,又不想依赖整套 Arrow 实现。[3] 这个接口为此定义了 plain C 的 schema 与 array 结构,带有生产方提供的 release callback,也保留 opaque private data。这样一来,生命周期规则可以跨库传递,不同运行时也能继续保持独立。[3] 换到实际交接里说,这份约定已经越过“这些 buffer 语义相同”,进入“数据跨过库边以后,所有权怎样交、何时释放、双方怎样避免踩坏彼此内存”。

这一层比“zero-copy”三个字更重要。zero-copy 的价值,取决于接收端是否能正确理解排放方式和生命周期。前提一松,复制会在后面重新出现,内存安全也会变成隐藏成本。Arrow 的 C Data Interface 要处理的正是这件事:buffer 长得一样还不够,生产方与消费方还得说清楚“谁拥有这块数据、何时释放”。[3]

规范还说明,这个接口服务的既有一次性交付,也有持续对话。schema 可以在开始时先传一次,后面的 batch 再以 array 形式逐步交付;文档还把设计目标同 Python buffer protocol 的经验并列,强调低适配成本的数据交换。[3] 到了这里,Arrow 已经越过单纯的内存格式,更接近分析系统之间的通用交接语言。

3. Flight 把同一份约定带过网络

Arrow Flight 有用,正因为数据离开进程之后,这份约定仍然保留下来。

Flight 规范把它定义成一个基于 gRPC 与 Arrow IPC 的高性能 RPC 框架,核心对象是 Arrow record batches 的流,旁边再配上用于发现和检查的元数据方法。[4] 这样读,Flight 的范围比“SQL over Arrow”更窄,也避开了替代所有 REST 或数据库协议的想象。它的承诺很具体:两边本来就理解 Arrow 数据时,可以继续用 Arrow 单位对话,同时加入传输、端点发现和应用自己的控制方法。[4]

这里需要说清楚:Flight 没有让网络免费,也没有让序列化凭空消失。字节依然要移动,帧依然存在,传输问题也仍在。它带来的变化更具体:生产端面对远端消费方时,仍可保持列式 batch;消费端也可以继续按 record batches 接收。[2][4]

所以 Flight 特别适合某些数据服务形态。协议可以用 descriptor 描述数据集,用 FlightInfo 暴露端点,再把 Ticket 交给客户端,通过 DoGet 拉取一条流,或者通过 DoPut 上传一串批次。[4] 它把意见鲜明地放在正确位置上:让传输语义尽量贴着 Arrow 的数据模型,上面那一层少生出另一套完全不同的脑内地图。

4. 最有力的证据,是别的引擎继续选择 Arrow

对 Arrow 最有力的证明,来自别的工具愿意把 Arrow 兼容性当成工程桥梁。这比单张性能图更有分量。

DuckDB 的 Python 指南写得很明确:DuckDB 可以直接对 Arrow Tables、Arrow Datasets 以及 RecordBatchReaders 跑 SQL,数据来源可以是 PyArrow,也可以来自 pandas 与 Polars,前面省去先导入一份 DuckDB 自有存储的步骤。[5] 这其实是很重的架构信号。DuckDB 仍然保留自己的执行引擎、优化器与存储选择;到了交换这一层,它愿意在 Arrow 已经存在的地方接入数据。[5]

Polars 从 dataframe 这一侧给出的判断也接近一致。它的 Arrow producer/consumer 指南推荐 Arrow PyCapsule Interface 和底层 Arrow C Data Interface,因为这套做法有机会实现 zero-copy 交换,也能避免强制依赖 pyarrow 或直接依赖 Polars。[6] 成功的共同约定就该长成这样:系统各自保留路线图和实现选择,只在数据跨过库边时同意交接方式。

顺着这些文档往下读,我更愿意把 Arrow 的战略价值理解成一种解耦能力。[3][5][6] DuckDB 继续做查询引擎,Polars 继续做 dataframe 引擎,服务层或传输层也继续独立存在;但分析型 batch 在它们之间移动时,各家私有内存方言带来的摩擦会少很多。

5. Arrow 最适合在哪里发力,团队又最容易在什么地方误读它

Arrow 最强的场景,是团队确实存在一条多引擎分析路径:Python 加 Rust,dataframe 加查询引擎,本地进程加远程数据服务,或者一个库生产 batch,另一个库消费 batch。[1][3][4][5][6] 在这些环境里,共享布局能省掉大量原本只是搬运与重塑形状的浪费。

Arrow 的适用面,集中在分析型交接处。存储设计、OLTP 的行式问题、事务协调,以及应用级 schema 演进,仍然由各自系统处理。zero-copy 的成立范围也需要按场景逐条核对:类型一致性、所有权处理、嵌套值边缘情况,以及实现完整度,都会决定复制或不稳定是否重新回来。[2][3][6]

所以真正该问的问题更窄:系统里哪一处交接正在为数据翻译反复付税,交接两边是否愿意同时接受 Arrow 的布局与生命周期规则? 若答案是愿意,Arrow 往往能拿掉一大截摩擦;若共识还没形成,口号就会先跑到架构前面。

Apache Arrow 真正的成功,不在于它又造出了一套更快的库,而在于它造出了一份足够结实的契约,让别的系统愿意持续到这里来会合。

来源

  1. Apache Arrow,《Overview》——项目范围、语言覆盖,以及 Arrow 作为通用列式内存层的定位。
  2. Apache Arrow Format,《The Arrow Columnar Format Specification》——validity bitmap、offsets buffer、record batches 与 64-byte 对齐规则。
  3. Apache Arrow Format,《The Arrow C Data Interface》——producer/consumer 交换、release callback、schema 与 array 交接,以及低适配成本目标。
  4. Apache Arrow Format,《Arrow Flight RPC》——基于 gRPC 的传输、record-batch 流、descriptor、ticket 与 DoGet/DoPut 模式。
  5. DuckDB 文档,《SQL on Arrow》——DuckDB 直接查询 Arrow Tables、Datasets 与 RecordBatchReaders 的方式。
  6. Polars 用户指南,《Arrow producer/consumer》——Arrow PyCapsule 与 C Data Interface 互操作、zero-copy 交换,以及更轻的依赖面。
  7. Wikimedia Commons 文件页——本文使用的 Wes McKinney 会议照片来源。
Previous PostgreSQL 的 MVCC,本质上是一份清理契约:一篇关于快照、元组版本与为什么 vacuum 属于并发模型内部的视频策展 Next Helm 在 2026:一篇关于 HIP、发布纪律与让打包工具保持可信的克制的治理信号

Recommended In oss

Matched by subject and format