Ballista:把 DataFusion 应用搬到集群,代价是接受版本落差
Apache DataFusion Ballista 分布式查询引擎。 Ballista 在集群中运行相同的 SQL 和 DataFrame 工作负载,只需最少的代码更改即可获得相同的结果。
秒懂
- 它是什么?
- Ballista 是 Apache DataFusion 的分布式执行引擎,让原有 SQL 和 DataFrame 代码几乎不改就能跨节点并行。它保留了 Spark 式的阶段划分和任务调度模型,但文档明确提醒:DataFusion 与 Ballista 之间存在功能落差,可能带来不兼容。
- 适合谁用?
- Ballista 适合三类人:已经用 DataFusion 写单机查询、现在需要处理更大数据量的开发者;熟悉 Spark 阶段模型、想换一个更轻的 Rust 原生引擎的团队;以及想复用调度器、执行器和计划序列化模块来构建专用查询引擎的库作者。不适合追求 DataFusion 最新特性立即可用的人,因为 README 明确说 DataFusion 和 Ballista 之间存在功能落差,可能带来不兼容。
- 能商用吗?
- 可以。Apache-2.0 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 2 天前。
- 用什么语言写的?
- 主要是 Rust(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月14日)和我们的分析,不构成法律意见。
开源项目深度解析
一个具体问题:单机 DataFusion 的边界
DataFusion 是一个内存中的查询引擎,单机性能不错,但数据量超过单机内存后,查询会失败或需要手动分片。Ballista 解决的是这个问题:它把 DataFusion 的查询计划拆成多个阶段,分发到不同节点的 executor 上并行执行,最后汇总结果。目标用户很明确:已经写了 DataFusion 代码、不想重写业务逻辑的人。README 给出的示例显示,从单机切换到分布式只需要把 `SessionContext::new()` 改成 `SessionContext::standalone().await?`,其余代码不变。这个承诺听起来很诱人,但文档紧接着用醒目提示警告:DataFusion 和 Ballista 之间存在功能落差,可能带来不兼容。这不是小事,意味着某些 SQL 函数或 DataFrame 操作在分布式模式下可能行为不同或直接不支持。
架构:调度器、执行器与阶段划分
Ballista 的集群由两类进程组成:一个或多个 scheduler,一个或多个 executor。scheduler 负责接收客户端提交的作业,把查询计划切成多个 stage,stage 之间以 shuffle 边界为分界。每个 stage 包含多个 task,每个 task 对应一个分区,executor 按 vcore 数量并行执行 task。这个模型和 Spark 高度相似,README 明确说 Spark 用户能直接理解。执行器执行完任务后向 scheduler 报告状态,scheduler 再调度下一批任务。客户端不直接连接 executor,所有交互都经过 scheduler,这简化了网络拓扑,但也让 scheduler 成为单点瓶颈。文档没有说明 scheduler 如何做高可用,这是一个需要自行验证的点。
启动方式:从 standalone 到集群部署
最简单的启动方式是调用 `SessionContext::standalone()`,它会在后台启动所有必要的 Ballista 基础设施,包括调度器和执行器,全部在进程内运行。这种方式适合开发和测试,但不适合生产。生产部署有两种路径:Docker Compose 和 Kubernetes。官方提供了对应的部署指南,Docker 镜像已经打包好 native 二进制。Cargo features 控制功能开关:客户端 crate 的 `standalone` 特性默认开启,关闭后可以连接外部集群。scheduler 的 `build-binary` 特性默认开启,用于构建带 CLI 和日志的二进制。如果你需要 Substrait 计划支持或 Prometheus 指标,需要显式启用 `substrait` 和 `prometheus-metrics` 特性,默认不开启。这意味着开箱即用的监控能力有限,需要自己编译。
Web TUI:浏览器里的监控界面
Ballista 提供了一个基于浏览器的 Web TUI,用于监控运行中的集群。当 scheduler 的 HTTP 端点可用时,在浏览器打开 scheduler 地址,比如 `http://localhost:50050`,会自动重定向到 Web TUI。这个界面展示 jobs、executors、metrics 和 scheduler 信息。对于运维人员来说,这是一个实用的功能,不用额外安装客户端工具。但文档没有说明 TUI 的实时性如何,也没有提到是否支持历史查询回溯。如果你需要详细的性能分析,可能还需要配合 Prometheus 指标,而后者需要编译时开启 `prometheus-metrics` 特性。这增加了一点部署复杂度,但不算大问题。
版本落差:最大的风险点
README 里有一句加粗的警告:DataFusion 和 Ballista 之间存在 gap,可能导致不兼容,社区正在努力缩小这个差距。这是整个项目最需要认真对待的一句话。Ballista 的版本号跟着 DataFusion 走,但两者不是同步发布的。如果你升级 DataFusion 到新版本,Ballista 可能还没跟上,或者反过来。这意味着你不能随意升级任一组件。对于依赖最新 DataFusion 特性的用户,这可能是一个硬性障碍。另一个风险是 Cargo features 里的 `force_hash_collisions`,它只用于测试,强制所有值哈希到相同位置,用来验证 shuffle 的正确性。如果你不小心在生产环境开启了它,性能会灾难性下降。文档明确标注这是 testing-only,但值得记住。
一个替代方案:直接使用 DataFusion 的并行能力
如果只是单机数据量超过内存,不一定需要 Ballista。DataFusion 本身支持多线程执行,可以利用多核 CPU。Ballista 引入的是网络调度和跨节点数据传输,这带来了额外的序列化和通信开销。对于中等规模的数据,单机 DataFusion 配合合理的内存配置可能更简单。另一个思路是使用 Spark,如果你已经在用 Spark SQL,迁移到 Ballista 需要重写部分代码,但执行模型是相似的。Ballista 的优势是 Rust 原生、内存占用更低、部署更轻。但 Spark 有更成熟的生态和更丰富的优化器。选择哪一个,取决于你对语言生态的偏好和集群规模。如果集群只有几台机器,Ballista 的复杂度可能不值得。
维护与升级成本
Ballista 是 Apache 项目,许可证是 Apache-2.0,这意味着你可以自由使用、修改和分发,只要保留版权声明。但维护成本不低:你需要跟踪 DataFusion 的版本更新,因为 Ballista 的版本号与 DataFusion 对齐,但功能可能滞后。每次升级 DataFusion,都要验证 Ballista 是否兼容。Cargo features 的设计让你可以裁剪功能,但这也意味着你需要理解每个 feature 的作用。例如,`arrow-ipc-optimizations` 默认开启,用于改善 shuffle 性能,但如果你关闭它,性能会下降。`spark-compat` 特性可以启用 Spark 兼容模式,但需要额外依赖 datafusion-spark。这些选择增加了配置复杂度。对于一个小团队,维护一个 Ballista 集群需要专门的运维知识。
编辑结论
Ballista 适合三类人:已经用 DataFusion 写单机查询、现在需要处理更大数据量的开发者;熟悉 Spark 阶段模型、想换一个更轻的 Rust 原生引擎的团队;以及想复用调度器、执行器和计划序列化模块来构建专用查询引擎的库作者。不适合追求 DataFusion 最新特性立即可用的人,因为 README 明确说 DataFusion 和 Ballista 之间存在功能落差,可能带来不兼容。在采用前,先确认你的 DataFusion 版本与 Ballista 的匹配关系,检查你依赖的 SQL 函数或 DataFrame API 是否在 Ballista 中已实现,并跑一遍你的核心查询。对于生产部署,优先考虑 Docker Compose 或 Kubernetes 方式,并验证 Web TUI 是否满足监控需求。如果你只需要单机并行,直接使用 DataFusion 本身即可,Ballista 的分布式调度开销没有意义。
社区笔记