本地数据分析的阵地战正在发生质的变化。2026年10月6日,高性能数据分析框架Polars正式推出2.0版本,不仅将底层惰性查询引擎默认切换为流式执行器,开启了基于80%内存阈值的磁盘溢出机制,还将SQL查询推至一级入口。官方同步放出基于AWS c7a实例的TPC-H与TPC-DS基准战报,直言在绝大多数测试中击败了嵌入式分析领域的标杆DuckDB与Apache DataFusion。
这不再只是一次单纯的Python数据处理库版本迭代,而是Polars向DuckDB腹地发起的一场正面突袭。过去数年,数据分析界形成了明确的分工默契:DataFrame编程归Polars,单机嵌入式SQL分析归DuckDB。Polars 2.0试图用同一套自研引擎同时吞下这两块领地。但在光鲜的性能跑分与技术口号背后,这场激进升级掩盖了未完工的落盘机制、失真的测试环境,以及由用户承担的高昂迁移代价。
跑分反超背后:精心剪裁的胜负天平
在官方公布的基准测试中,Polars展现出了凶悍的扩展能力。在AWS c7a.4xlarge(16核、32GB内存)与裸金属实例c7a.metal(192核、384GB内存)的对比中,面对100级数据规模(SF100),从16核扩展至192核让Polars在TPC-H上的总耗时提升了3.8倍,TPC-DS总耗时提升2.2倍。同期DuckDB 1.5.6分别提升3.2倍与1.9倍,DuckDB 2.0 alpha(2.0.0.dev2610011535)提升2.2倍与1.5倍,而DataFusion 54.0.0仅提升1.7倍与1.0倍。
表面上看,这是一场完胜。然而细看测试方法论,官方设定充斥着利好自身架构的温室条件。测试规约采取每查询热跑5次取最优值,跨引擎虽然清除系统缓存,但单引擎在连续查询之间保留了Parquet文件缓存。更关键的是,评测直接将DataFusion发生超时(TPC-DS Q72、Q67)与内存溢出(TPC-H Q18)的查询从所有引擎的统计项中剔除,而不是施加惩罚分数。这种选择性清洗虽然维持了平均数的可比性,却掩盖了各引擎在极限深层嵌套查询下的真实容错表现。
并发调度的反常特征也暴露了新引擎的硬伤。在10级数据规模(SF10)下,将Polars推上192核巨型实例不仅没有提速,反而因固定的高并发线程调度开销而出现迟滞,TPC-DS耗时甚至比16核慢了1.8倍;直到将线程人为限制在32核,其性能才回到第一梯队。高并发调度在轻量查询上的反噬,说明其执行器在负载感知与资源收敛上仍显粗糙。
磁盘溢出的半程拼图与实测落差
Polars 2.0最引人注目的卖点,是声称默认开启了磁盘溢出(Out-of-core)并设定了64GB磁盘配额。长久以来,内存耗尽(OOM)崩溃是阻碍开发者在生产流水线中使用Polars的最大痛点,而成熟的磁盘落盘机制正是DuckDB赖以成名的护城河。
然而现实存在明显的工程落差。Polars 2.0当前的磁盘溢出机制仅是一张完成了半程的拼图。代码实现中,真正支持溢出落盘的只有排序(sort)、窗口函数以及部分表达式;在实际分析场景中吞噬内存最剧烈的大表关联(join)与分组聚合(group-by),目前仍停留在开发路线图上,并未在2.0版本中兑现。
决定单机引擎生死存亡的不是排序能吞多少数据,而是关联计算何时耗尽内存。
历史测试与第三方极限验证更直接戳破了免崩溃神话。在2025年5月的官方PDS-H历史测试中,SF10下Polars流式引擎以3.89秒领先DuckDB的5.87秒,但放量到SF100时,DuckDB便以19.65秒反超Polars流式引擎的23.94秒。
更严苛的第三方实测进一步放大了这种短板:在20核CPU、15 GiB内存与4 GiB交换分区的严苛受限环境下执行10亿行单表聚合,DuckDB平稳完成落盘溢出,耗时22.73秒处理完14.8 GiB数据;而Polars惰性执行在内存水位冲到12.51 GiB时,直接被操作系统的OOM Killer强行终止。
- 风险.如果数据管线依赖大表Join或高基数Group-by,升级Polars 2.0并不能防止内存溢出击垮任务,单机分析防线的稳固程度依然不及DuckDB。
激进的破坏性变更:谁在承担行序与类型代价
为了强行推行流式引擎与严苛类型体系,Polars 2.0在兼容性上挥出了重重一斧。这一改动对生产环境的破坏力,远超过多数团队的预期。
在2.0版本中,对LazyFrame调用collect()或异步collect_async()时,默认的执行引擎解析参数engine='auto'会被底层重定向至流式引擎。这一切换直接打破了行序确定性。在默认配置下,join、group_by与unpivot等关键操作不再保证输出行顺序的稳定。对于依赖时序窗口、特征工程前后一致性或严格数据审计的流水线而言,这意味着如果没有显式声明maintain_order=True,下游消费数据将出现静默的顺序漂移。
类型系统与API的清理同样决绝:
Arrow、Parquet与Iceberg中的映射结构被重新设计,原先解析为List(Struct)的模式直接切断,强制加载为原生的pl.Map类型。老脚本中所有基于list或struct命名空间的处理逻辑在运行时直接报错,必须重构为pl.col('m').map.entries()。
此外,历史API遭到了彻底清扫。melt()被彻底废弃,强制替换为unpivot();with_row_count()被with_row_index()取代;LazyFrame.fetch()与profile()被直接移除,运行时直接抛出专门的AttributeRemovedError与ArgumentRemovedError异常。类型运算上,有符号整数与UInt64的混合计算不再容忍隐式转换为Float64,而是严格向上推导至Int128;is_in()中的隐式有损转换被全面封死为异常中断。
- 建议.现有企业数据流水线切勿急于全局升级,应在CI链路中针对关键特征提取逻辑校验行序确定性,并全面排查已废弃算子。
Polars借由2.0吹响了跨界进攻的号角,其查询优化器所引入的Join重排序、公共子计划消除与动态布隆过滤器,确实代表了纯Rust现代执行器的第一流工程水准。但在单机分析的竞技场上,打赢一场参数经过精心修剪的热缓存基准测试,与在真实世界恶劣的资源约束下交付坚不可摧的稳定性,从来都是两回事。当内存大坝面临决口时,开发者手中那张只拼了一半的落盘拼图,恐怕还不足以成为高枕无忧的避风港。
