DuckDB 源码分析概述
DuckDB 完全用 C++ 实现,代码仓库托管在 GitHub。截至 2026 年,项目拥有超过 30 万行 C++ 代码,代码质量极高,架构清晰,是学习现代列式数据库实现的绝佳素材。
本文从 DuckDB 源码分析的角度,带你深入了解其架构设计、核心模块和工作原理。
代码仓库结构
克隆仓库后,顶级目录组织如下:
duckdb/
├── src/ # 核心源码
│ ├── include/ # 头文件
│ ├── common/ # 通用工具和类型系统
│ ├── storage/ # 存储引擎
│ ├── execution/ # 执行引擎
│ ├── optimizer/ # 查询优化器
│ ├── parser/ # SQL 解析器
│ ├── planner/ # 查询计划器
│ ├── function/ # 内置函数
│ └── main/ # 入口和数据库管理
├── extension/ # 扩展(JSON, HTTPFS, ICU 等)
├── test/ # 测试代码
├── tools/ # 工具(CLI, Python, Node.js 等绑定)
├── benchmark/ # 基准测试
├── Makefile # 构建文件
└── CMakeLists.txt # CMake 构建配置
核心目录详解
从 DuckDB 源码分析的角度,以下目录最为关键:
| 目录 | 职责 | 关键文件 |
|---|---|---|
src/storage/ | 数据持久化、缓冲池、表存储 | table_manager.cpp, buffer_manager.cpp |
src/execution/ | 查询执行、向量化处理 | executor.cpp, operator.cpp |
src/optimizer/ | 查询优化、统计信息 | optimizer.cpp, statistics |
src/parser/ | SQL 解析、语法树构建 | parser.cpp, transformer.cpp |
src/planner/ | 逻辑计划构建 | planner.cpp, logical_operator.cpp |
src/function/ | 聚合、标量、表函数 | aggregate, scalar, table |
构建系统与编译
从源码构建
# 克隆仓库
git clone https://github.com/duckdb/duckdb.git
cd duckdb
# Release 构建(推荐)
make
# Debug 构建(开发调试用)
make debug
# 指定并行度加速编译
make -j$(nproc)
# 编译后的二进制
./build/release/duckdb
CMake 选项
# 启用扩展
cmake -DBUILD_PARQUET=1 -DBUILD_JSON=1 -DBUILD_HTTPFS=1
# 启用测试
cmake -DBUILD_UNITTESTS=1
# 编译优化级别
cmake -DCMAKE_BUILD_TYPE=Release # 或 Debug, RelWithDebInfo
构建过程分析
DuckDB 的构建系统值得关注的点:
- 单一组件编译:编译产物是单个
duckdb可执行文件,链接时通过unity build加速 - 扩展动态加载:扩展可以编译成
.duckdb_extension文件,运行时加载 - 测试框架:使用 DuckDB 自研的测试框架
test/unittest
存储引擎架构
DuckDB 的存储引擎是**列式(columnar)**的,这是它比 SQLite 等行式数据库快 10-100 倍的根本原因。
存储层级
Database File (.duckdb)
├── Catalog (元数据)
│ ├── Schemas
│ ├── Tables
│ ├── Columns (列式存储)
│ └── Indexes
├── Data
│ ├── Row Groups (行组,每组约 10 万行)
│ │ ├── Column Segments (列段)
│ │ └── Statistics (统计信息,用于查询过滤)
│ └── Persistent Storage
└── WAL (Write-Ahead Log)
列式压缩
DuckDB 支持多种列式压缩算法,源码在 src/storage/compression/:
// 源码中的压缩类型枚举(简化)
enum class CompressionType : uint8_t {
UNCOMPRESSED,
CONSTANT, // 常量压缩
RLE, // 行程编码
DICTIONARY, // 字典压缩
BITPACKING, // 位打包
FSST, // 快速静态符号表
CHIMP, // 时间序列压缩
PATAS // 自适应时间序列压缩
};
缓冲池管理
BufferManager 是存储引擎的核心组件,源码在 src/storage/buffer_manager.cpp:
// BufferManager 核心职责(基于源码分析):
// 1. 管理内存中的数据块(Buffer)
// 2. 处理磁盘与内存之间的页面交换
// 3. 实现 LRU 淘汰策略
// 4. 支持直接 IO 和内存映射文件
class BufferManager {
// 关键方法
BlockHandle* RegisterBlock(BlockId block_id);
void UnregisterBlock(BlockId block_id);
DataPointer Pin(BlockHandle* handle);
void Unpin(BlockHandle* handle);
};
执行引擎架构
DuckDB 的执行引擎采用**向量化(Vectorized)**模型,这是它高性能的关键。
Volcano 迭代器模型
SQL Query
↓
Parser (解析 SQL)
↓
Planner (生成逻辑计划)
↓
Optimizer (优化逻辑计划)
↓
Physical Plan (生成物理计划)
↓
Executor (向量化执行)
↓
Result
向量化执行
与传统数据库逐行处理不同,DuckDB 每次处理一批数据(Vector),批量大小为 STANDARD_VECTOR_SIZE(默认 2048 行)。
// 源码中的 Vector 结构(简化)
struct Vector {
VectorType type; // FLAT, CONSTANT, DICTIONARY, SEQUENCE
LogicalType logic_type; // INTEGER, VARCHAR, DOUBLE...
data_ptr_t data; // 实际数据指针
ValidityMask validity; // NULL 值掩码
SelectionVector* sel; // 选择向量(用于过滤)
};
// 操作符处理 Vulkan 的方式
void FilterOperator::Execute(DataChunk &input, DataChunk &result) {
// 一次处理整个 chunk(2048 行)
// 通过 SelectionVector 记录符合条件的行
// 无需逐行判断,CPU 缓存友好
}
执行流水线
// 源码中的执行流水线示例
// Pipeline: Scan → Filter → Aggregate → Output
// ↓ ↓ ↓
// 读取 2048 行 过滤 2048 行 聚合结果
// ↓ ↓ ↓
// 向量化读取 SIMD 过滤 并行聚合
查询优化器
DuckDB 的优化器在 src/optimizer/ 中,执行一系列优化规则:
// 优化器规则执行顺序(源码中定义)
void Optimizer::RunOptimizer() {
// 1. 表达式重写
expression_rewriter->Rewrite(plan);
// 2. 谓词下推
filter_pushdown->PushDown(plan);
// 3. 连接顺序优化
join_order_optimizer->Optimize(plan);
// 4. 列剪枝
column_binding_manager->Prune(plan);
// 5. 子查询消除
subquery_flattener->Flatten(plan);
// 6. 统计信息优化
statistics_propagator->Propagate(plan);
}
统计信息驱动的优化
DuckDB 存储行组的列级别统计信息(min/max/null_count),优化器利用这些信息进行:
- 分区裁剪:根据 min/max 跳过不相关的行组
- 基数估计:选择最优的 Join 顺序
- 查询计划选择:决定是否使用索引或全表扫描
SQL 解析器
DuckDB 的 SQL 解析器位于 src/parser/,使用手写递归下降解析器(而非 Yacc/Bison):
// 解析过程
// SQL: SELECT a, b FROM t WHERE c > 10
// ↓
// Parser::ParseQuery(sql_string)
// ↓
// Transformer (转换 SQL token 为 AST 节点)
// ↓
// SelectStatement (SELECT 语句的 AST 表示)
// ├── select_list: [ColumnRef(a), ColumnRef(b)]
// ├── from_table: BaseTableRef(t)
// └── where_clause: Comparison(c, >, 10)
class SelectStatement : public SQLStatement {
unique_ptr<SelectNode> node;
// SelectNode 包含: select_list, from_table, where_clause,
// group_expressions, having, order_expressions, limit...
};
扩展机制
DuckDB 的扩展架构非常灵活,核心扩展包括:
-- 安装和加载扩展
INSTALL httpfs;
LOAD httpfs;
INSTALL json;
LOAD json;
INSTALL parquet;
LOAD parquet;
INSTALL icu; -- Unicode 支持
LOAD icu;
INSTALL fts; -- 全文搜索
LOAD fts;
INSTALL spatial; -- 空间数据处理
LOAD spatial;
扩展的源码在 extension/ 目录下,每个扩展有自己的目录结构:
extension/
├── parquet/ # Parquet 读写
├── json/ # JSON 支持
├── httpfs/ # S3/HTTP 文件系统
├── icu/ # 国际化
├── fts/ # 全文搜索
└── spatial/ # 空间数据
性能设计原则
从 DuckDB 源码分析中,可以总结出其高性能设计的几个核心原则:
- 向量化执行:一次处理 2048 行,最大化 CPU 缓存利用率
- 列式存储:只读取查询需要的列,减少 IO
- 统计信息过滤:利用列级 min/max 跳过无关数据块
- MMAP 优化:大文件使用内存映射,避免显式 IO
- 编译优化:使用 C++ 模板元编程和编译时计算
- SIMD 加速:关键路径使用 SIMD 指令集(AVX2/NEON)
如何深入源码
# 推荐阅读路径(按从易到难)
# 1. 从 main 入口开始
src/main/database.cpp # 数据库启动流程
src/main/connection.cpp # 连接和查询执行
# 2. 理解核心类型系统
src/common/types/ # 类型系统
# 3. 阅读解析和计划
src/parser/ # SQL 解析
src/planner/ # 查询计划
# 4. 深入存储引擎
src/storage/table/ # 表存储
src/storage/checkpoint/ # 检查点
# 5. 探索执行引擎
src/execution/operator/ # 各种操作符实现

相关文章
📘 博客: https://duckdblab.org #DuckDB #源码分析 #数据库架构 #C++