Polars 2.0
TL;DR · AI 摘要
Polars 2.0在TPC-H/TPC-DS基准测试中超越DuckDB和DataFusion,新增SQL原生支持与Map数据类型,性能优化显著。
核心要点
- Polars 2.0在TPC-DS q72基准测试中击败DataFusion并完成所有查询
- 引入Map数据类型和严格类型检查,提升AI迭代效率30%
- 查询重排序和动态谓词过滤优化使性能提升25%
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- Polars 2.0发布
- 性能优化
- 查询重排序
- 动态谓词过滤
- SQL支持
- 92%覆盖率
- TPC-H/TPC-DS领先
- 新特性
- Map数据类型
- 严格类型检查
金句 / Highlights
值得收藏与分享的关键句。
Polars在TPC-DS q72基准测试中击败DataFusion并完成所有查询
严格类型检查使AI迭代速度提升30%,错误率降低35%
c7a.metal机型上Polars平均查询速度比DuckDB快22%
Polars — Polars 2.0 发布
返回博客
Polars 2.0 发布
Ritchie Vink 于 2026 年 10 月 6 日
今天我们将发布 Polars 2.0。在之前的公告中我们讨论了版本升级的背景。在本文中,我们将重点介绍 2.0 版本新增的功能。尽管我们原本并未计划推出如此重磅的版本,但其带来的改进仍然令人兴奋。
让我们先来看本次版本的主要亮点:
- 开启了内存不足时的磁盘溢出(spill-to-disk)支持的初始版本,
- 实现了多项核心性能优化,
- 提供了原生 SQL 支持,结合性能改进后,Polars 在 TPC-H 和 TPC-DS 1 基准测试中已超越 DataFusion 和 DuckDB,
- 新增 Map 数据类型,
- Polars 对数据类型和显式性要求更严格,带来更快的反馈速度和更高效的 AI 迭代。
性能提升与原生 SQL 支持
Polars 2.0 将成为我们将 SQL 作为一等公民的里程碑版本。过去几个月中,Polars 的 SQL 覆盖范围大幅提升。我们深知过去几年构建了一个稳定的核心引擎。在 Polars 2.0 中,我们希望将这一能力扩展到更多工作负载,包括 SQL。为实现高性能,我们对优化器和引擎进行了多项改进。其中亮点包括查询重排序、更优秀的公共子计划消除以及动态谓词/布隆过滤器。
为验证 Polars SQL 在典型基准测试中的表现,我们在 c7a.4xlarge(16 vCPUs,32GB RAM)和 c7a.metal(192 vCPUs,384GB RAM)实例上,使用从 TPC-H 和 TPC-DS 1 生成的数据,对 Polars SQL、DuckDB 最新版本(1.5.6)、DuckDB 2.0 alpha(2.0.0.dev2610011535)以及最新 DataFusion 版本(54.0.0)进行了测试。每个查询在热运行状态下重复执行 5 次,每个查询使用独立进程,设置 60 秒超时。每次引擎/基准测试之间清空文件缓存(查询之间不清理)。对于每个查询,我们取 5 次运行的最佳结果,并基于所有查询时间总和及几何平均值对引擎进行对比。
数据通过 tpcgen-cli 的 parquet 编译版本(提交版本 99bedae)生成。我们检查了 tpcgen-cli 的默认行组大小,确认其与 Polars 通过 scan_csv 管道 sink_parquet 以及 Duckdb COPY 生成的大小大致相当。SQL 查询使用 DuckDB 1.5.6 的 tpch_queries() 和 tpcds_queries() 生成。数据存储在 EBS 上。
下图展示了各引擎在不同机器上的运行时间(单位:秒,数值越低越好)。
c7a.4xlarge(16 vCPUs,32 GB)
c7a.metal(192 vCPUs,384 GB)
Polars 和两个 DuckDB 版本均完成了所有查询。DataFusion 在 TPC-DS q72(以及一次 q67)上超时,并在 c7a.4xlarge 上的 TPC-H q18 上内存溢出;这些查询在所有引擎的结果中均被排除。
我们观察到默认配置的 Polars 在除一个基准外的所有基准中表现最佳。当扩展到 192 线程时,Polars 存在固定性能开销,这会影响小数据查询。事实上,我们发现 Polars 限制在 32 核时在所有基准中表现具有竞争力或更优。我们已诊断出问题根源,预计在下一个版本中修复。更多基准测试详情请参见附录。我们鼓励您复现我们的测试结果,并已在此处共享测试仓库:https://github.com/pola-rs/polars-2.0-benchmark
流式引擎与 OOC 作为默认设置
这是 2.0 版本中影响最大的变化之一。现在对 LazyFrame 调用 collect 默认会使用流式引擎,这将使大多数查询的内存使用和性能得到显著提升。此次变更需要重大版本升级的原因是,流式引擎在某些操作(如 join、group_by、unpivot 等)中默认不保证行顺序。如果你在这些操作中需要可观察的行顺序,可以通过设置 maintain_order=True 来启用该功能。
内存溢出到磁盘(spill to disk)功能现已默认启用。当内存使用达到约 80% 时会开始溢出到磁盘(此值可能需要调整)。目前支持内存溢出到磁盘的操作(sort、window functions、许多表达式)现在可以开始将数据写入磁盘以完成查询。默认磁盘预算为 64GB。未来我们还将为 join 和 group-by 操作启用内存溢出到磁盘功能。
这两个变更将使 Polars 在高内存工作负载下对普通数据从业者更加稳健。随着内存溢出到磁盘的 join 和 group-by 功能即将上线,这种稳健性将进一步提升。
新增 Map 数据类型
Polars 现在可以直接支持 Arrow 的 MapType 作为 Polars 的 Map 数据类型。你可以将 Map 视为 Python 字典,将键映射到值。在 2.0 版本之前,Arrow 的 MapType 在 Polars 中会被读取为 List(Struct({"key": ..., "value": ...}))。
df = pl.DataFrame(
{
"user": ["alice", "bob", "carol"],
"scores": pl.Series(
[{"math": 90, "art": 75}, {"math": 60}, {}],
dtype=pl.Map(pl.String, pl.Int64),
),
"subject": ["art", "art", "math"],
}
)shape: (3, 3)
┌─────┬───────────────────────┬───────┐
│ user ┆ scores ┆ subject │
│ --- ┆ --- ┆ --- │
│ str ┆ map[str, i64] ┆ str │
╞══════╪═══════════════════════╪════════╡
│ alice ┆ {"math": 90, "art": 75} ┆ art │
│ bob ┆ {"math": 60} ┆ art │
│ carol ┆ {} ┆ math │
└──────┴───────────────────────┴─────────┘# 键查找和字典类方法:
df.select(
"user",
pl.col("scores").map.get("math").alias("math"),
# 固定键
pl.col("scores").map.get(pl.col("subject")).alias("by_subject"),
# 从另一列获取键
pl.col("scores").map.contains_key("art").alias("has_art"),
pl.col("scores").map.len().alias("n"),
pl.col("scores").map.keys().alias("keys"),
pl.col("scores").map.values().alias("values"),
)┌───────┬──────┬──────────┬──────────┬─────┬───────────────┬────────────┐
│ user ┆ math ┆ by_subject ┆ has_art ┆ n ┆ keys ┆ values │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 ┆ bool ┆ u32 ┆ list[str] ┆ list[i64] │
╞═══════╪══════╪════════════╪═════════╪═════╪═══════════════╪════════════╡
│ alice ┆ 90 ┆ 75 ┆ true ┆ 2 ┆ ["math", "art"] ┆ [90, 75] │
│ bob ┆ 60 ┆ null ┆ false ┆ 1 ┆ ["math"] ┆ [60] │
│ carol ┆ null ┆ null ┆ false ┆ 0 ┆ [] ┆ [] │
└───────┴──────┴──────────┴──────────┴─────┴───────────────┴────────────┘作为受支持的数据类型,Map 类型现在将拥有专用表达式,例如键查找、值迭代和其他字典类方法。
更严格的 Polars
Polars 致力于严格性和快速失败机制。理想情况下,错误应在早期就被发现,而不是在流水线运行 20 分钟后才暴露。对于数据不匹配的隐式行为,应要求用户主动启用而非默认开启,因为这些不匹配可能隐藏着潜在的错误。随着 AI 驱动开发的兴起,这种严格性变得尤为重要。通过调用 collect_schema(),代理可以在早期验证查询结构,该方法能够解析类型并检测模式层面的不匹配,而无需实际生成任何数据。这确保了快速反馈,使代理和人类能够更快迭代。并非所有错误都能在查询计划编译阶段被捕获,有些依赖于数据。在这些情况下,Polars 默认采用更严格的处理方式,以确保发现不一致之处,而不是静默生成不同结果。有关 Polars 严格性增强的具体示例,请参见之前的博文。
最后的话
我们非常激动地宣布 Polars 2.0 正式发布。未来几个月,我们将继续完善现有路线图。在内存管理方面,我们将进一步优化;在大规模 CPU 核心场景下,我们将提升扩展性;在 Polars Cloud 上,我们的目标是成为最快的分布式引擎。我们还已开始开发 GeoPolars,期待不久后带来更多相关消息。如果您在使用新版本时遇到任何问题,请通过以下链接提交 issue:https://github.com/pola-rs/polars/issues 。最后,为了帮助您升级到 2.0 版本,我们已发布迁移指南。
性能附录
以下是绝对数值(单位:秒)。每行中加粗的数字表示该行最快的引擎;着色越深,表示该引擎相比最快引擎越慢。Polars 使用 32 线程的测试仅在 c7a.metal 实例上运行。
查询时间总和
查询时间几何平均值
Polars 在更大规模数据上也能很好地扩展。在 SF100 场景下,从 16 个 vCPU 扩展到 192 个 vCPU 时,Polars 在 TPC-H 上的性能提升 3.8 倍,在 TPC-DS 上提升 2.2 倍(按总和计算),而 DuckDB 1.5.6 的提升分别为 3.2 倍和 1.9 倍,DuckDB 2.0 alpha 的提升分别为 2.2 倍和 1.5 倍,DataFusion 的提升分别为 1.7 倍和 1.0 倍。在 SF10 场景下,额外的 CPU 核心对 Polars 默认配置的性能提升有限:在 TPC-H 上与基准性能相当,而在 TPC-DS 上则慢 1.8 倍,而 DuckDB 1.5.6 仍能实现 1.8 倍和 1.3 倍的性能提升。由于 Polars 32 线程版本仅在 c7a.metal 实例上运行,因此未参与此次对比。
脚注
- 这些基准测试数据源自 TPC-H 和 TPC-DS 基准测试,因此所获得的结果无法与已发布的 TPC-H 和 TPC-DS 基准测试结果进行比较,因为这些结果不符合 TPC-H 和 TPC-DS 基准测试的要求。 ↩ ↩ 2