你在一张表里统计每位用户的订单,本来只是一次普通汇总。可一旦用户多到数千万,电脑就得同时记住数千万份小计,内存很快见底。Polars 2.1 处理的正是这类时刻:内存放不下时,不再直接失败,而是把部分中间结果暂存到磁盘,稍后读回来继续计算。
Polars 是一个开源 DataFrame 引擎——可以把 DataFrame 理解成带列名的电子表格,分析师和数据工程师用它清洗、连接和汇总数据。2.1 的关键不只是一项性能改动。它同时推进了超内存计算、SQL、湖仓接入和文件加密。不过,本文所有效果与功能信息都来自 Polars Team 的发布博客,尚无第三方基准或独立复现。
最容易爆内存的分组,终于能“借用”磁盘
Polars 2.0 已默认支持 spilling——当内存紧张时,把中间数据溢写到磁盘。但高基数 group-by 一直是缺口。Group-by 就是按类别分组后求和、计数;“高基数”表示不同类别特别多。每个类别都要保留一份计算状态,因此它常常是内存耗尽的源头。
据 Polars Team 的示例,2.1 可以对流式 group-by 做 out-of-core 执行,也就是处理规模不再严格受物理内存限制。引擎会用 shuffle 重新分配部分分组状态,并在需要时写入磁盘。官方把内存预算设为 200 MB,对 3000 万个分组同时计算求和与去重计数。日志记录了 312 次 spill,spill 成功率为 100.0%,写入和读回的数据各为 132.8 MB,查询最终完成。
这里最容易误读的是“100.0%”。它只是溢写操作的成功率,不是查询提速 100%,也不是节省了 100% 内存。磁盘访问通常比内存慢。这项能力的价值更像安全阀:任务可能变慢,但有机会继续跑完,而不是在内存边界前直接中止。
类似能力也扩展到了流式 window node,也就是 SQL 或表达式里的 over 窗口计算。窗口计算会在保留每行的同时,按指定分组或顺序计算统计值。2.1 允许它以 out-of-core 方式执行。新环境变量 POLARS_OOMKILL_THRESHOLD_FRACTION 则让使用者设置内存不足时的终止阈值,避免进程无限逼近危险区域。
SQL 不再只是换一种写法
2.1 继续把 SQL 当作一等接口。最醒目的新增项是 recursive CTE,即 WITH RECURSIVE:一段查询可以反复引用此前生成的结果,逐层展开有层级或链式关系的数据。
这次还加入了临时表、ASOF JOIN ... MATCH_CONDITION、更多相关与非相关子查询,以及 generate_series、range、REGEXP_REPLACE、DATE_TRUNC 等函数。相关子查询会引用外层查询的数据,非相关子查询则可以独立运行。版本也开始支持带时区的时间戳,以及 CURRENT_DATE、CURRENT_TIME 和 CURRENT_TIMESTAMP。这些更新让更多工作可以直接用 SQL 表达,不必先拆成多段 DataFrame 操作。
从文件分析走向湖仓管理
另一条主线是 Apache Iceberg。Iceberg 是管理湖仓表的一层规则:底下仍是一批数据文件,上面则负责表结构、快照和并发更新。官方称,Polars 2.1 已能端到端管理 Iceberg 表,包括创建、写入、查询和删除。
具体来说,使用者可以把 Iceberg catalog——记录表与文件位置的目录——挂到 SQLContext,在已挂载目录中创建或删除表,也能覆盖指定分区、按需创建表或命名空间。读取侧加入了 Rust resolver,并可利用 Iceberg 统计信息、复用 catalog 和缓存 REST catalog 扫描。
Parquet 也补上了安全能力。Parquet 是按列存放数据的文件格式,适合分析型读取;2.1 可以读取和写入加密 Parquet,为静态文件内容增加保护。版本还加入实验性的 GeoParquet 读写,用于带地理空间信息的 Parquet 数据。
为什么这次升级值得看
这次发布的意义,在于几条过去相对分散的能力开始接上:执行引擎能在内存不足时把状态转移到磁盘;SQL 能表达递归和更复杂的子查询;Iceberg 接口覆盖表的生命周期;Parquet 文件则有了加密读写。
它没有证明单机从此可以无代价地处理任意规模的数据。更准确的说法是,Polars 把“内存不够就失败”的硬边界,改造成了一条可以用磁盘、时间和工程配置继续周旋的边界。
升级前还有两项兼容性变化需要留意。Polars 2.1 不再支持 Python 3.10。eager 模式——立即执行并返回结果的模式——也禁止字符串与数值混合算术:例如 pl.Series([1, 2]) + "4" 过去会得到字符串 "14"、"24",现在会抛出异常,与 lazy 模式保持一致。
局限与未知
- 官方只展示了一个 200 MB 内存预算、3000 万分组的示例,没有披露不同数据分布、磁盘条件和失败场景下的表现,也没有第三方复现。
- 溢写扩大了可处理范围,但材料没有给出相对纯内存执行的速度代价、额外磁盘空间需求或完整适用边界。
- 加密、
register_catalog、delete_iceberg和 masked NumPy 转换仍属不稳定 API,后续版本可能调整。