土法炼钢 · 系统与基础设施

【列存引擎内核】DuckDB 架构与嵌入式 OLAP

文章导航

分类入口
databasearchitecture
标签入口
#duckdb#embedded-olap#row-group#column-segment#pg-duckdb#columnar-storage

目录

DuckDB 自称「SQLite for analytics」:进程内、无服务器、列向量化 OLAP——与 ClickHouse 列存基础(第 01 篇) 的 Server + MergeTree 模型形成对照。Python/R/Julia 通过库直接 import duckdb,SQL 在宿主进程内编译执行;数据可在 .duckdb 文件、内存或外部 Parquet/CSV。

本文建立 DuckDB 1.x 架构心智模型:Catalog → Optimizer → Parallel Pipeline → Column Segment 存储。不排名「谁更强」——第 13 篇 谈选型;本篇锚定 嵌入式分析 路径。

版本锚定:DuckDB 1.x(官方 Internals 文档与 main 分支源码结构)。


一、部署模型:in-process vs server

维度 ClickHouse Server DuckDB
进程 独立 daemon 嵌入宿主(Python/JVM/CLI)
网络协议 TCP/HTTP/MySQL 协议 无(API / CLI)
并发模型 多 client 连接 单进程内多线程 pipeline
存储默认 MergeTree Part 目录 单文件 DB + Row Group
典型场景 集群 OLAP、日志仓 笔记本 EDA、ETL、PG 扩展
flowchart TB
  subgraph ch [ClickHouse]
    SRV[clickhouse-server]
    MT[MergeTree Parts]
    SRV --> MT
  end
  subgraph duck [DuckDB Embedded]
    APP[Python / R / CLI]
    ENG[DuckDB Engine]
    DB[(database file)]
    APP --> ENG --> DB
  end

工程结论:需要 多租户远程 SQL 服务 → ClickHouse(或 Trino 等);需要 脚本内嵌分析、零运维 → DuckDB。


二、进程内架构分层

DuckDB 源码顶层(A 级:https://duckdb.org/docs/internals/overview):

目录(约) 职责
Parser / Binder src/parser/, src/planner/binder/ SQL → 逻辑计划
Optimizer src/optimizer/ 谓词下推、JOIN 重排
Execution src/execution/ 物理计划、Pipeline
Storage src/storage/ Table、Row Group、Segment
Catalog src/catalog/ 元数据、类型
Transaction src/transaction/ MVCC 简化模型

查询路径:

flowchart LR
  SQL[SQL] --> P[Parser]
  P --> B[Binder]
  B --> O[Optimizer]
  O --> PP[Physical Plan]
  PP --> PL[Pipelines]
  PL --> RG[Row Groups / Segments]

与 ClickHouse Parser → Analyzer → QueryPlan → Processors 同构,但 无 Distributed 远程子计划(联邦另说)。


三、Storage:Table → Row Group → Column Segment

DuckDB 持久化列存与 ClickHouse Part 不同:

ClickHouse MergeTree DuckDB
Part 目录 + 每列 .bin/.mrk Row Group 内 Column Segment
后台 merge 合成大 Part Checkpoint 合并 + vacuum
primary.idx 稀疏索引 Zone map / statistics

3.1 Row Group

默认约 122880 行 一组(版本可能调整,以 STORAGE_ROW_GROUP_SIZE 或文档为准)。Row Group 是 并行与压缩 的单元——类似 ClickHouse Granule(第 02 篇) 的上层容器。

3.2 Column Segment

每列在每个 Row Group 内一段 Column Segment

flowchart TB
  T[Table]
  T --> RG1[Row Group 0]
  T --> RG2[Row Group 1]
  RG1 --> C1[col_a segment]
  RG1 --> C2[col_b segment]
  RG2 --> C3[col_a segment]
  RG2 --> C4[col_b segment]

3.3 Checkpoint

DuckDB 周期性 checkpoint 把内存表数据刷入文件、整理 WAL 式日志(实现细节见 src/storage/checkpoint/)。对比 ClickHouse 始终写 Part + 异步 merge——DuckDB 更偏 单文件 SQLite 生命周期

3.4 外部文件:Parquet / CSV

SELECT count(*) FROM read_parquet('data/*.parquet');
CREATE TABLE t AS SELECT * FROM read_parquet('events.parquet');

零拷贝式分析:不 import 也可查 Parquet——列存格式对齐 DuckDB 向量化读(见 第 12 篇 Pipeline)。


四、Catalog 与类型系统

DuckDB Catalog 管理 schema、表、宏、序列。嵌入式场景常:

import duckdb
con = duckdb.connect('analytics.duckdb')
con.execute("CREATE TABLE hits AS SELECT * FROM read_parquet('hits/*.parquet')")
特性 说明
嵌套类型 STRUCT, LIST, MAP —— 分析 JSON 友好
ENUM 低基数 string
扩展 LOAD httpfs; LOAD icu; 社区扩展

类型丰富度利于 数据科学;ClickHouse 用 NestedJSON 类型(版本差异)应对类似需求——语法不互通。


五、事务与并发

DuckDB 支持 ACID 事务(单写者多读者模型演进中,1.x 文档强调并发读 + 写串行化程度因版本改进)。与 ClickHouse 无经典 OLTP 事务 对比鲜明。

操作 DuckDB ClickHouse MergeTree
BEGIN/COMMIT 支持 不支持跨语句事务
UPDATE/DELETE 支持(重写 Row Group) Mutation 异步
并发写 受限 高 ingest

边界:DuckDB 不是高并发 OLTP 替代品;ClickHouse 不是银行核心账务库。


六、扩展生态:httpfs、postgres_scanner、pg_duckdb

6.1 httpfs / S3

INSTALL httpfs;
LOAD httpfs;
SET s3_region='us-east-1';
SELECT * FROM read_parquet('s3://bucket/data/*.parquet');

云数据湖 探查 场景 DuckDB 极常出现——无需起集群。

6.2 postgres_scanner

INSTALL postgres;
LOAD postgres;
ATTACH 'dbname=postgres user=...' AS pg (TYPE POSTGRES);
SELECT * FROM pg.public.orders JOIN local_summary USING (id);

联邦查询在 DuckDB 进程内 完成——对比 ClickHouse PostgreSQL table engine 或 jdbc 桥。

6.3 pg_duckdb

PostgreSQL 扩展 pg_duckdb(社区/官方协作演进)把 DuckDB 嵌入 PG 进程执行分析查询——「PG 权限 + DuckDB 向量执行」。与 ClickHouse 作 PG 副本 是不同架构:


七、内存模型

DuckDB 默认 aggressive 使用可用内存做 hash join / aggregate spill 前缓冲。Settings 如:

嵌入式 Python 中常:

con.execute("SET memory_limit='4GB'")
con.execute("SET threads=4")

无 fabricated 性能数字——笔记本与服务器差异极大。


八、与 ClickHouse 存储对照表

机制 ClickHouse DuckDB
最小 IO 单元 Granule + Mark Row Group + Segment
合并 MergeTree merge Checkpoint
主键 ORDER BY 排序键 无强制 PK;索引 via ART 等扩展
跳数索引 minmax/set/bloom Segment stats + zone maps
分布式 Distributed + Keeper 无内置;靠外部调度
压缩 CODEC per column Automatic + 列压缩

读路径对照 查询读取路径(第 05 篇)第 12 篇


九、适用场景边界

9.1 DuckDB 主场

9.2 不应 sole 依赖 DuckDB


十、CLI 与文件布局

duckdb analytics.duckdb -c "SELECT version()"

数据库文件内含:

不像 ClickHouse data/db/table/part/ 可直接 ls 教学——教学 Part 格式用 clickhouse-local(第 02 篇);DuckDB 用 PRAGMA database_size 等 introspec。

PRAGMA database_size;
SELECT table_name, estimated_size FROM duckdb_tables();

输出须本地执行后解读。


十一、源码阅读路线

  1. src/storage/data_table.cpp — Table 入口
  2. src/storage/table/row_group.cpp — Row Group
  3. src/storage/table/column_segment.cpp — Segment 压缩与 scan
  4. src/execution/physical_operator.cpp — 算子基类
  5. src/parallel/executor.cpp — 并行调度(衔接第 12 篇)

官方 Internals PDF/网页与源码注释一致处为 A 级证据。


十二、实验框架(须本地执行)

12.1 Row Group 观察

CREATE TABLE rg_demo AS SELECT range AS id, id % 7 AS g FROM range(500000);
PRAGMA show_tables;
-- 查 storage 相关 pragma(版本以文档为准)

12.2 Parquet 联邦

COPY (SELECT * FROM range(1000000)) TO '/tmp/rg_test.parquet' (FORMAT PARQUET);
SELECT sum(id) FROM read_parquet('/tmp/rg_test.parquet');

12.3 与 ClickHouse 导出互操作

# ClickHouse 侧(若环境有)
clickhouse-client -q "SELECT * FROM t FORMAT Parquet" > t.parquet
duckdb -c "SELECT count(*) FROM 't.parquet'"

不声称转换性能——仅验证格式互通。


十三、学术谱系:嵌入式分析 · DuckDB · 列存三角闭合

阶段 文献 / 系统 本篇落点
2005 MonetDB/X100;C-Store 列存 + 向量化坐标(第 01 篇)
2019 Raasveldt & Mühleisen, DuckDB, SIGMOD demo 嵌入宿主进程的分析引擎
工程 SQLite 定位对照 「分析版 SQLite」叙事;非 OLTP 替代
本系列 与 CH Server 分工 第 13 篇决策树

谱系结论:DuckDB 把 X100 式执行放进 库文件 + 进程内 API;ClickHouse 把 C-Store/RS 式 Part 放进 服务与副本。二者闭合第 01 篇「列存三角」的嵌入角与服务角。

13.1 工程间隙与开放问题

论文 / demo 设定 生产嵌入
单用户分析会话 多线程应用共享同一 .duckdb 文件锁
少谈持续 ingest 高写入仍弱于 CH

开放问题:嵌入式引擎与湖仓文件(Parquet)是否应默认「无本地 .duckdb」?可检验:纯 read_parquet vs 导入本地库的运维成本(入口:DuckDB SIGMOD’19;第 12–13 篇)。


十四、小结

DuckDB = 嵌入宿主进程的列向量化分析引擎;Storage 以 Row Group / Column Segment 为核心,checkpoint 维护文件健康。理解其与 ClickHouse Server + MergeTree + Distributed 的分工,是 OLAP 技术选型的第一步。

附录 A、Column Segment 生命周期

  1. Insert 积累内存 buffer;2. Row Group 满则 compress 写 segment;3. Checkpoint 将 catalog 与 segment pointer 持久化;4. Vacuum/optimize 合并小 segment(视版本 API)。读路径:statistics → 可能 skip → decompress → vector。

附录 B、压缩栈

DuckDB 列 segment 常 轻量编码(RLE/FOR 等)+ 通用 codec。与 CH 第 03 篇 显式 CODEC() 不同,DuckDB 默认自动选择——DDL 控制少,Parquet 外链保留源 compression。

附录 C、嵌套类型示例

CREATE TABLE events (id BIGINT, props STRUCT(url VARCHAR, status INTEGER));
SELECT props.url FROM events WHERE props.status >= 500;

STRUCT/LIST 利于 semi-structured;ClickHouse 用 JSON/Tuple 类型,SQL 不互通。

附录 D、扩展列表(1.x 常见)

扩展 用途
httpfs / aws S3/HTTP Parquet
postgres PG attach
icu 排序/.locale
json JSON 函数增强
parquet Parquet 读写

INSTALL ext; LOAD ext; — 离线环境需预装。

上一篇物化视图

下一篇DuckDB 向量化与 Pipeline

参考资料

核心论文

  1. Raasveldt & Mühleisen, DuckDB: an Embeddable Analytical Database, SIGMOD 2019 demo(A/B 级)。
  2. Boncz et al., MonetDB/X100, CIDR 2005(执行坐标;A 级)。
  3. Stonebraker et al., C-Store, VLDB 2005(列存坐标;A 级)。

规范 / 源码 / 文档

  1. DuckDB Documentation, Internals(1.x;A 级)。
  2. DuckDB Source, src/storage/src/execution/(A 级)。
  3. ClickHouse Documentation — 部署对照(A 级)。

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-07-07 · database / distributed

【分布式 OLAP 查询引擎】引擎选型与数据平台阅读地图

用决策树收束 Trino/Spark/ClickHouse/DuckDB/DataFusion/PostgreSQL 的适用边界:交互式联邦、批 ETL、嵌入式分析、流批一体各走哪条路径;给出能力对照表(无吞吐排名)与 postgresql→columnar→lakehouse→stream→query-engine 全栈阅读顺序,闭合数据平台栈。

2026-06-18 · database / architecture

【列存引擎内核】物化视图与增量管道

ClickHouse Materialized View 的触发语义、块级增量与目标表引擎选择;Kafka Engine + MV 典型架构;与 PostgreSQL 触发器/MV 的对照及常见坑。


By .