DuckDB vs Polars 对比
下面是这两个现代数据处理工具的核心对比:
| 维度 | DuckDB 1.5.4 | Polars 1.42.1 | 赢家 |
|---|---|---|---|
| 核心接口 | SQL(PostgreSQL 风格) | Python 表达式 API + 懒加载 DataFrame | 看个人偏好 |
| 编写语言 | C++ | Rust | — |
| 许可证 | MIT | MIT | — |
| 执行模式 | 向量化,超内存自动溢出到磁盘 | 向量化,流式引擎(2026 稳定) | 平手 |
| 大于 RAM 数据 | 是(自动溢出) | 是(流式引擎) | 平手 |
| 文件原地读取 | Parquet, CSV, JSON, Iceberg, Delta | Parquet, CSV, JSON, Iceberg | DuckDB 领先 |
| 持久化存储 | 原生 .duckdb + DuckLake | 无(仅计算) | DuckDB |
| 最佳应用场景 | SQL 分析、临时文件查询 | Python 数据转换、ETL 管道 | — |
性能对比
总体印象:在大多数工作负载上,两者的差异比营销宣传的要接近。
具体数据
在 2 TB Parquet 数据集上:
– DuckDB:约 45 秒
– Polars(默认模式):约 60 秒
– Polars(强制异步):约 100 秒
读取 10 GB Parquet 单个文件:
– DuckDB:2–6 秒
– Polars:1.5–5 秒(略胜一筹)
复杂 SQL 查询(500M 行数据):
– DuckDB:8–25 秒
– Polars:10–35 秒(DuckDB 略胜)
记忆使用
在 500M 行数值数据上:
– DuckDB:2–6 GB
– Polars:1.5–5 GB(略微领先)
关键点:内存是真正的分界线。DuckDB 处理超大文件时自动溢出到磁盘,Polars 在流式模式下也能处理超内存数据,但两者各有优劣。
编程范式对比
DuckDB(SQL 优先)
import duckdb
result = duckdb.sql("""
SELECT customer_id, sum(amount) AS revenue
FROM 'data/orders/*.parquet'
GROUP BY customer_id
ORDER BY revenue DESC
LIMIT 5
""").show()
Polars(DataFrame 优先)
import polars as pl
result = (
pl.scan_parquet("data/orders/*.parquet")
.group_by("customer_id")
.agg(pl.col("amount").sum().alias("revenue"))
.sort("revenue", descending=True)
.limit(5)
.collect()
)
使用场景推荐
| 场景 | 推荐 |
|---|---|
| SQL 分析、BI 仪表板 | DuckDB |
| Python 原生 ETL 管道 | Polars |
| 快速文件查询、临时分析 | DuckDB |
| 长链式数据转换 | Polars |
| 超 TB 级别数据(需自动溢出) | DuckDB |
| 需要持久化存储 | DuckDB(.duckdb 数据库) |
Polars 适合的工作负载
Polars ≈ 本地的、单机的、高性能的 Spark
换个角度:
Pandas → Polars(快 10-100 倍,加上优化器)
↓
Spark(分布式版本,支持超大数据)
✅ 你的数据完全放在一台机器上(即使是 100 GB,现代服务器 RAM 便宜了)
✅ 你需要快速迭代开发(毫秒级反馈)
✅ 本地笔记本 + 云服务器上运行 Python 脚本
# Polars:秒级启动,毫秒级查询
df = pl.read_parquet("sales.parquet")
result = df.filter(pl.col("amount") > 1000).group_by("customer_id").agg(...)
# 立即得到结果