DuckDB:笔记本上跑分析 SQL,比 pandas 快 10x

起因

数据分析常见情境:

  • 收到一堆 CSV / Parquet(几 GB - 几十 GB)
  • 想跑 SQL JOIN / 聚合 / 窗口函数分析
  • 没 Snowflake / BigQuery(个人项目 / 本地探索)
  • pandas 慢 + groupby 写得难看

DuckDB:嵌入式 OLAP 数据库("SQLite for analytics"),单文件
binary,跑分析 SQL 跟 columnar 仓库一样快,在你笔记本上

pip install duckdb           # Python
brew install duckdb          # CLI

CLI

$ duckdb my.db
D SELECT * FROM 'data.csv' LIMIT 5;     -- 直接读 csv,无需 import
D SELECT COUNT(*) FROM 'data.parquet';
D SELECT a.x, b.y FROM 'a.csv' a JOIN 'b.parquet' b ON a.id = b.id;

CSV / Parquet / JSON 直接当 table 查,无 import 步骤。

Python

import duckdb

# in-memory
con = duckdb.connect()

# 直接查 CSV
df = con.execute("""
    SELECT country, SUM(amount) AS total
    FROM 'orders.csv'
    WHERE qty > 5
    GROUP BY country
    ORDER BY total DESC
""").df()       # 返回 pandas df

# 持久化
con = duckdb.connect('analysis.db')
con.execute("CREATE TABLE orders AS SELECT * FROM 'orders.csv'")

跟 pandas / DataFrame 互通

import pandas as pd
import duckdb

# pandas df 直接当 table 用(DuckDB zero-copy 引用)
df = pd.read_csv('big.csv')
result = duckdb.sql("""
    SELECT col1, AVG(col2)
    FROM df            -- 直接引用 pandas df
    GROUP BY col1
""").df()

polars 同样:

import polars as pl
pl_df = pl.read_csv('big.csv')
result = duckdb.sql("SELECT * FROM pl_df WHERE col1 > 100").pl()

DuckDB 跟 pandas / polars / Arrow 数据zero-copy 互转(都用 Arrow
columnar 内存格式)。

性能

8 核 16 GB 笔记本,10 GB Parquet 文件:

SELECT country, SUM(amount), COUNT(*)
FROM 'orders.parquet'
GROUP BY country
ORDER BY 2 DESC
LIMIT 10;
工具 时间
pandas 35s
polars (eager) 8s
polars (lazy) 4s
DuckDB 2.5s

DuckDB 列存 + vector 执行 + 多核 + 全局优化器,把分析查询打得很快。

直接查远程 Parquet

duckdb.sql("""
    SELECT *
    FROM read_parquet('s3://my-bucket/orders/*.parquet')
    WHERE date = '2025-03-01'
""")

DuckDB 支持 S3 / GCS / Azure / HTTP 直读。
配合 partition + Parquet column pruning → 只读必要的 column 和 partition。

数据湖直接查

跟 Iceberg / Delta lake 集成:

INSTALL iceberg;
LOAD iceberg;

SELECT * FROM iceberg_scan('s3://bucket/table/');

不用 Spark 也能查 Iceberg。

window 函数 / 复杂 SQL

SELECT
    user_id,
    date,
    amount,
    SUM(amount) OVER (PARTITION BY user_id ORDER BY date) AS cum_sum,
    RANK() OVER (PARTITION BY DATE_TRUNC('month', date) ORDER BY amount DESC) AS rank_in_month
FROM orders;

全 SQL 标准 + Postgres 兼容大量扩展 + DuckDB 特有的 ANTI/SEMI JOIN /
QUALIFY 等。

EXPORT / IMPORT

COPY (SELECT * FROM big_table) TO 'out.parquet' (FORMAT PARQUET);
COPY (SELECT * FROM big_table) TO 'out.csv' (HEADER, DELIMITER ',');

数据格式互转的瑞士军刀。

真实 case:替代 pandas EDA

数据探索,原本:

df = pd.read_csv('events.csv')
df_filtered = df[df.user_age > 18]
grouped = df_filtered.groupby(['country', 'product']).agg({
    'amount': ['sum', 'mean'],
    'qty': 'count',
}).reset_index()
sorted = grouped.sort_values(('amount', 'sum'), ascending=False)
sorted.head(20)

DuckDB 等价:

duckdb.sql("""
    SELECT country, product,
           SUM(amount) AS total,
           AVG(amount) AS avg_amount,
           COUNT(*) AS n
    FROM 'events.csv'
    WHERE user_age > 18
    GROUP BY country, product
    ORDER BY total DESC
    LIMIT 20
""").df()

SQL 更直白 + 跑得快 + 不需要 import 完整 csv。

跟 Snowflake / BigQuery 对比

DuckDB Snowflake BigQuery
部署 单 binary SaaS SaaS
数据规模 < 1 TB(单机) PB PB
成本 0 按 credit 按扫描 GB
启动 < 100ms < 1s < 5s
SQL Postgres-like ANSI++ ANSI+
并发用户

DuckDB 不替代 Snowflake(不是 multi-user / 不是无限 scale)。
但 90% 个人 / 团队分析(< 1 TB)DuckDB 够 + 免费 + 快。

motherduck(DuckDB cloud)

DuckDB 团队也做了 motherduck.com → DuckDB + cloud sync:

  • 本地查 + 云端永久存
  • 共享数据集
  • 跨设备一致

按需用,但 DuckDB 本身完全离线可用。

嵌入应用

# Django / FastAPI 里嵌 DuckDB 做 analytics endpoint
import duckdb

@app.get('/analytics/top-products')
def top_products():
    return duckdb.sql("""
        SELECT product, SUM(amount) AS total
        FROM read_parquet('s3://.../orders/*.parquet')
        WHERE date > current_date - 7
        GROUP BY product
        ORDER BY total DESC
        LIMIT 10
    """).df().to_dict('records')

不用 separate analytics DB / 全部嵌进应用。

extension 生态

INSTALL httpfs;        -- HTTP / S3
INSTALL spatial;       -- 地理空间
INSTALL fts;           -- 全文搜索
INSTALL postgres;      -- 查 Postgres 表
INSTALL excel;         -- 读写 .xlsx
INSTALL sqlite;        -- 读写 SQLite 文件

INSTALL postgres; LOAD postgres; 后:

ATTACH 'host=pg.example.com dbname=app user=...' AS pg (TYPE postgres);
SELECT * FROM pg.public.orders LIMIT 10;

把 Postgres 表当本地表查 + JOIN 本地 CSV → 异构数据查询。

踩过的坑

  1. 大数据 > RAM 时:DuckDB 用 disk spilling 但仍可能慢。SET memory_limit='10GB',留剩余给 OS。

  2. column type 自动推断错:CSV 列 sometime "N/A",DuckDB 推断
    string。read_csv_auto(..., types={'col': 'INTEGER'}) 显式。

  3. 更新慢:DuckDB 是 OLAP,不适合频繁 UPDATE。点查 / 单行更新 →
    用 SQLite。

  4. 并发写不行:单写者,多 reader。Web app 多 worker 同时写 DuckDB
    会锁。

  5. extension 版本不匹配:DuckDB 升级后 extension cache 旧版本
    报错。FORCE INSTALL <ext> 强制更新。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。