DuckDB — 嵌入式列存 OLAP
待复核DuckDB 是 CWI(阿姆斯特丹国家数学与计算机研究中心)团队从 2019 年开始用 C++ 写的 “列式 SQLite”——一个单文件、嵌入式、专门做分析查询(OLAP)的数据库。
日常类比:
- sqlite 是抽屉里的工作手册——事务好、写入快、但每次”统计上个月所有订单”它要逐行翻
- DuckDB 是装在同一个抽屉里的列式分析机——同样不需要服务器,但聚合、扫描、连接快上百倍
它不是要替代 SQLite(事务型)或者 postgresql(通用型),而是补一个之前没人做的位置:单机、嵌入式、专做分析。
不理解 DuckDB,下面这些事都没法解释:
- 为什么过去两年数据科学界突然把它捧成爆款——Pandas 和 Polars 都加了 DuckDB SQL 后端
- 为什么”一个 binary,无服务器”还能比 postgresql 单机分析快十倍
- 为什么 CSV/Parquet 文件可以直接当表查,不需要先 import
- 为什么和 clickhouse 同样是列存,定位完全不同
DuckDB 的”快”建立在三个选择上:
-
列式存储:传统数据库一行一行存(行式),统计
SELECT avg(price)要把每行整条读出来。列式把同一列的所有值连续存——只读 price 这一列,IO 量直接砍掉 90%。这点和 clickhouse 同源。 -
向量化执行(Vectorized Execution):传统执行引擎一次处理一行(“火山模型”),函数调用开销大。DuckDB 一次处理一批(向量,约 2048 行),CPU cache 友好、能用 SIMD 指令并行算。
-
嵌入式:和 sqlite 一样,DuckDB 是一个 lib,不是服务器。Python 里
pip install duckdb就能用,没有端口、没有进程、没有运维。C/C++/Python/JS/Rust/R/Java 都有 binding。
加分项:直接读外部文件——CSV、JSON、Parquet、甚至 postgresql / MySQL 表都能当成 DuckDB 的表查,零 ETL。
案例一:Python 里直接 SQL 查 CSV
Section titled “案例一:Python 里直接 SQL 查 CSV”import duckdb
# 不需要 load 进 DataFrame,直接查result = duckdb.sql("SELECT category, avg(price) FROM 'sales.csv' GROUP BY category").df()这段代码做了三件事:
- 解析
sales.csv的 schema(自动推断列类型) - 列式读取(只读 category 和 price 两列,跳过其他列)
- 返回 Pandas DataFrame
对比传统做法 pd.read_csv 后再聚合——DuckDB 在大文件(GB 级)上经常快 5-10 倍。
案例二:CLI 直接查 S3 上的 Parquet
Section titled “案例二:CLI 直接查 S3 上的 Parquet”duckdb mydb.duckdb -c "SELECT count(*) FROM read_parquet('s3://bucket/logs/*.parquet')"read_parquet 是 DuckDB 内置函数,支持通配符、HTTP、S3(查 S3 前需 INSTALL httpfs; LOAD httpfs; 并配好凭证)。不需要先下载、不需要 import,SQL 就是 ETL。常用来做”一次性大数据探索”。
案例三:和 Pandas 互操作
Section titled “案例三:和 Pandas 互操作”import pandas as pdimport duckdb
df = pd.read_csv("orders.csv")con = duckdb.connect()con.register("orders", df) # 把 DataFrame 当表注册
result = con.execute(""" SELECT customer_id, sum(amount) FROM orders WHERE date >= '2025-01-01' GROUP BY customer_id""").df()register 是零拷贝——DuckDB 直接读 Pandas 的内存布局,不复制数据。这是 Polars / Pandas 都拥抱 DuckDB 的关键:不抢用户的 DataFrame,做它擅长的 SQL 那部分。
-
多线程默认开但有时不生效:DuckDB 默认用所有核心,但单 query 太小或数据太分散时调度反而拖慢。可以
SET threads=1对照测试。 -
大数据集要设 memory_limit:默认会把中间结果留在内存,超过 RAM 直接 OOM。生产环境一定
SET memory_limit='4GB',超出就自动 spill 到磁盘。 -
SQL 方言和 PostgreSQL 不完全一致:DuckDB 大体兼容 PostgreSQL,但一些函数名、日期处理细节有差异。比如
date_trunc参数顺序、字符串拼接行为。从 PG 迁移过来要逐条测。 -
Parquet 写入分区策略要手动指定:
COPY ... TO 'output' (FORMAT 'parquet', PARTITION_BY (year, month))必须显式声明分区列,否则就是单文件。和 Spark 写 Parquet 的默认行为不同。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 数据科学家本地分析(CSV / Parquet / Pandas)
- 单机 OLAP 探索(< 1TB 数据)
- 嵌入到应用里做”内置分析模块”——比如 BI 工具、笔记应用
- 替代 SQLite 做”读多写少的分析报表”
- 替代部分 Spark 单机场景(数据 < RAM × 10)
不适用:
- 高并发事务型场景(用 postgresql 或 sqlite)
- 跨机器分布式分析(用 clickhouse / Spark / Trino)
- 需要严格 ACID 多用户写入(DuckDB 是单写多读)
- 实时流处理(DuckDB 是批处理,没有 streaming)
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2019 年:CWI 的 Mark Raasveldt 和 Hannes Mühleisen 启动项目。他们的观察是——数据科学家 90% 的时间用 Pandas,但 Pandas 在大数据上慢;同时数据库领域的列存技术(C-Store / clickhouse / Vectorwise)已经成熟,但都是服务器型。把列存做成嵌入式有市场。
- 2021 年:v0.2 发布,Python binding 成熟,开始在数据科学圈传播。
- 2023 年:v0.10 加 Iceberg 表格式支持,进入数据湖生态。
- 2024 年 6 月:v1.0 GA(Generally Available),稳定 API 和文件格式。
- 2022 年起:独立公司 MotherDuck 推出基于 DuckDB 的托管云仓,把”本地 DuckDB → 云端同方言”做成商业产品。
- 嵌入式数据库不只能做事务——SQLite 占了”事务嵌入式”,DuckDB 占了”分析嵌入式”,是补全而非替代
- 列存 + 向量化 是过去十年 OLAP 数据库的共同选择(clickhouse / Snowflake / BigQuery 都是这个思路)
- 零 ETL 是趋势——直接查 CSV/Parquet/远端表的能力比”先 import 再查”对用户友好得多
- 小而精胜过大而全——DuckDB 团队明确说”我们不做事务、不做分布式、不做流处理”,反而做出了独特的位置
- 官方文档:duckdb.org/docs(教程结构清晰,从 CLI 到 Python 全覆盖)
- 论文:DuckDB: an Embeddable Analytical Database, SIGMOD 2019(4 页演示论文,讲设计取舍)
- 视频:Mark Raasveldt — Inside DuckDB(CMU 数据库讲座,讲架构细节)
- sqlite —— 嵌入式数据库的鼻祖,DuckDB 的对照系
- clickhouse —— 同样是列存,但走分布式服务器路线
- postgresql —— 传统行存通用数据库,DuckDB 经常和它互补使用
- sqlite —— 同样嵌入式,但偏事务;DuckDB 偏分析
- postgresql —— DuckDB SQL 方言的主要参照,和 PG 单机分析做对比
- clickhouse —— 同样列式向量化,但 CH 是分布式服务器、DuckDB 是嵌入式
- arrow —— 内存列式标准;DuckDB 与 Pandas/Polars 互操作常经 Arrow
- cstore-2005 —— 列存分析的学术祖先;DuckDB 把同类思路做成嵌入式
- clickhouse —— ClickHouse — 把列存 OLAP 推到硬件极限
- columnar-storage-formats-2023 —— Columnar Storage Formats 2023 — Parquet/ORC 的体检报告
- snowflake-2016 —— Snowflake 2016 — 把数仓拆成 storage / compute / services 三层
- starrocks —— StarRocks — Doris 分叉出来的向量化 CBO 国产 OLAP
- velox-meta-2022 —— Velox — Meta 统一执行引擎
- arrow —— Apache Arrow — 内存列式标准
- arrow-rs —— arrow-rs — Apache Arrow / Parquet 的 Rust 参考实现
- dbt-core —— dbt-core — 把 SQL 当工程代码写,让数据仓库里的转换跑起来
- debezium —— Debezium — 把数据库的”刚刚改了”变成消息流
- duckdb-wasm —— duckdb-wasm — 把分析数据库塞进浏览器标签页
- evidence —— Evidence — 把 Markdown + SQL 编译成静态报告站
- kuzu —— Kùzu — 把图数据库做成 DuckDB
- lance —— Lance — AI 数据列存格式
- lightdash —— Lightdash — 寄生在 dbt 项目里的开源 BI
- marimo —— marimo — 反应式 Python 笔记本
- observable-framework —— Observable Framework — 编译期跑数据,浏览器只看结果
- pyarrow —— PyArrow — 让所有数据系统共用一块内存
- sqlite —— SQLite — 嵌入式 SQL 数据库
- starrocks —— StarRocks — MPP 列存数据库