项目:duckdb/duckdb | 星数:42674 | 语言:C++ | 协议:MIT | GitHub仓库
更新日期:2026-10-30 · 作者:CodeArts 编辑部
上个月运营发来一个 300MB 的订单导出文件,让我算"每个省份、每个品类、每个月的复购率"。用 pandas 打开,内存直接吃到 6GB,机器开始卡;导进 MySQL 光建表和导入就花了二十分钟。后来同事扔给我一句:用 DuckDB 试试。装完 pip,一行 SQL 直接读 CSV 出结果,全程不到十秒。
DuckDB 是 42674 星的嵌入式分析数据库,MIT 协议,C++ 实现。它没有服务器——进程内运行,像 SQLite 一样简单,但面向的是分析型(OLAP)场景:列式存储、向量化执行、自动并行。
| 维度 | SQLite / MySQL(OLTP) | DuckDB(OLAP) |
|---|---|---|
| 存储布局 | 行式 | 列式 |
| 擅长 | 按主键读写单行 | 全表聚合、大范围扫描 |
| 索引 | B+ 树为主 | 按需建,更依赖列存与压缩 |
| 典型查询 | 查订单详情 | 算月度 GMV 环比 |
| 并发写 | 支持 | 单写多读 |
搞清这条分界,就不会拿 DuckDB 当业务后端。它适合的是「读一大片数据做统计」,不适合「高频小事务」。用它替代线上订单库一定会出事。
# Python
pip install duckdb
# 命令行
brew install duckdb # macOS
winget install DuckDB.cli # Windows
# 一条命令直接查 CSV,不需要建表
duckdb -c "SELECT province, sum(amount) FROM read_csv_auto('orders.csv') GROUP BY 1 ORDER BY 2 DESC LIMIT 10"
read_csv_auto 会自己推断表头和类型,read_parquet、read_json_auto、Excel 同理。这一点非常省事——临时分析根本不用先建 schema。
用法一:直接在 pandas 上跑 SQL。DuckDB 能挂载已有的 DataFrame 对象,不用把数据搬来搬去:
import duckdb
import pandas as pd
con = duckdb.connect()
df = pd.read_csv('orders.csv')
con.register('orders', df)
res = con.sql("""
SELECT province, category, count(*) AS cnt,
round(avg(amount), 2) AS avg_amount
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY 1, 2
ORDER BY cnt DESC
""").df()
用法二:直接查 Parquet 目录。数据团队把日志按天写成 Parquet,DuckDB 可以用通配符一次性扫完,而且只读需要的列:
SELECT count(*), avg(duration)
FROM read_parquet('logs/*/*.parquet')
WHERE path LIKE '/api/%';
用法三:跨源联邦查询。同一条 SQL 里可以把本地 CSV、Postgres 表和 Parquet 文件 join 起来——联合分析场景里这个能力太省事了。
快的情况:单机内存装得下(或稍大一点,DuckDB 会溢写到磁盘)、查询是聚合与扫描、数据是 Parquet/CSV 这类列格式。
容易翻车的情况:
SET memory_limit='4GB' 和 SET temp_directory 让它溢写,但速度会明显下降。| 场景 | 推荐 |
|---|---|
| 单机一次性分析、几百 MB 到几十 GB | DuckDB |
| 多用户并发的实时分析服务 | ClickHouse |
| 数据小而需要复杂 Python 生态操作 | pandas |
| 嵌入式场景、随应用分发 | DuckDB(同 SQLite 定位) |
我的实际用法是:探索阶段全用 DuckDB,脚本一跑就出结果;定型的定时报表如果数据量增长到百 GB,再迁到 ClickHouse。这个迁移路径很顺,SQL 写法基本兼容。
不能。DuckDB 面向分析型负载,同一时刻只允许一个写进程,没有完善的用户权限与事务隔离机制,不适合高并发小事务。业务库请继续用 MySQL/Postgres。
pandas 是行式内存结构,聚合时要逐行处理且受 GIL 限制;DuckDB 用列式存储、向量化执行与多线程并行,只读需要的列,还能下推到 Parquet 的元数据跳过无关数据块。
设置 SET memory_limit 与 SET temp_directory,DuckDB 会把中间结果溢写到磁盘,牺牲部分性能换取可处理更大的数据。也建议先把中间结果落成 Parquet,减少重复计算。