← 中文资源

简体中文版本

Polars 惰性查询优化

用 Polars 惰性帧执行查询:谓词下推、投影裁剪与流式执行在数据移动前完成计划优化,聚合按声明自动编排。

阅读英文原文

示例说明

eager 模式下每个操作立即物化中间结果,链式转换会把整表反复重算。lazy 模式把整条查询编译成逻辑计划再优化:过滤条件推到扫描最近处执行,未使用的列在投影阶段裁掉,流式执行让内存峰值与表大小解耦——数据量从 GB 级起步时这些优化是质变的。

技术要点

  • 谓词下推把过滤推近数据源
  • 投影裁剪移除未引用列
  • streaming 让聚合内存恒定

代码解读

首个查询过滤浏览事件后按天分组,聚合表达式同时给出去重用户数、金额合计与众数事件,collect 的 streaming 开关启用流式执行。第二个查询演示查看优化后的逻辑计划——优化器已经把选择列收缩到两列并下推条件。

使用建议

collect 之前全部是声明,别在中间插入 eager 操作打断优化。复杂 join 也能受益于计划重排。大规模生产场景配合扫描文件源直接全链路流式。

代码示例

import polars as pl

events = pl.DataFrame({
    'user_id': [1, 2, 3, 4, 5, 6],
    'event': ['view', 'click', 'view', 'purchase', 'click', 'view'],
    'amount': [0, 0, 0, 120, 0, 0],
    'day': ['2026-10-21', '2026-10-21', '2026-10-22', '2026-10-22', '2026-10-22', '2026-10-22'],
})

lazy = events.lazy()

report = (
    lazy
    .filter(pl.col('event') != 'view')
    .group_by('day')
    .agg(
        active_users=pl.col('user_id').n_unique(),
        revenue=pl.col('amount').sum(),
        top_event=pl.col('event').mode().first(),
    )
    .sort('day')
    .collect(streaming=True)
)

print(report)

plan = (
    lazy.filter(pl.col('amount') > 0)
    .select(['day', 'amount'])
    .collect_optimized_plan()
)
print('optimized logical plan nodes:', len(str(plan).splitlines()))