市面上有许多数据库管理系统 (DBMS)。但不存在一种“万能”的数据库系统。所有系统都在不同的权衡中,以更好地适应特定的使用场景。DuckDB 也不例外。在此,我们将尝试解释 DuckDB 的目标,以及我们为何且如何通过技术手段来实现这些目标。首先,DuckDB 是一个支持结构化查询语言 (SQL) 的关系型(面向表)DBMS。
DuckDB 的主要特性
简单
SQLite 是世界上部署最广泛的 DBMS。其安装简单和嵌入式进程内操作是其成功的核心。DuckDB 采纳了这些关于简单性和嵌入式操作的理念。
DuckDB 没有任何外部依赖,无论是在编译期间还是运行时。对于发行版,整个 DuckDB 源代码树被编译为两个文件:一个头文件和一个实现文件,即所谓的“合并版本”(amalgamation)。这极大地简化了部署和在其他构建过程中的集成。要构建 DuckDB,只需要一个可用的 C++11 编译器即可。
对于 DuckDB,无需安装、更新和维护任何 DBMS 服务器软件。DuckDB 不作为单独的进程运行,而是完全嵌入在宿主进程中。对于 DuckDB 针对的分析型用例,这具有高速数据传输的额外优势。在某些情况下,DuckDB 可以直接处理外部数据而无需复制。例如,DuckDB 的 Python 包可以直接对 Pandas 数据运行查询,而无需导入或复制任何数据。
可移植
由于没有依赖项,DuckDB 极其易于移植。它可以针对所有主流操作系统(Linux、macOS、Windows)和 CPU 架构(x86、ARM)进行编译。它既可以部署在小型、资源受限的边缘设备上,也可以部署在拥有 100 多个 CPU 核心的大型多 TB 内存服务器上。使用 DuckDB-Wasm,DuckDB 甚至可以在 Web 浏览器和手机上运行。
DuckDB 为 Java、C、C++、Go、Node.js 等语言提供了 API。
功能丰富
DuckDB 提供强大的数据管理功能。它对 SQL 中的复杂查询有广泛支持,拥有庞大的函数库、窗口函数等。DuckDB 通过我们自定义的、针对批量优化的多版本并发控制 (MVCC) 提供事务保障(ACID 属性)。数据可以存储在持久化的单文件数据库中。DuckDB 支持二级索引,以加快查找单个表条目的查询速度。
DuckDB 与 Python 和 R 深度集成,可实现高效的交互式数据分析。
快速
DuckDB 旨在支持分析型查询工作负载,也称为联机分析处理 (OLAP)。这些工作负载的特征是复杂的、运行时间相对较长的查询,它们会处理存储数据集中很大一部分的数据,例如对整个表进行聚合或在多个大表之间进行连接。对数据的更改通常也是大规模的,涉及追加多行,或者同时更改或添加表的很大一部分。
为了高效支持这种工作负载,减少每个数值所消耗的 CPU 周期至关重要。目前数据管理领域实现这一点的最先进技术是向量化或即时 (JIT) 查询执行引擎。DuckDB 使用列式向量化查询执行引擎,查询虽然仍被解释执行,但一大批数值(即“向量”)是在一次操作中处理的。这极大地降低了传统系统(如 PostgreSQL、MySQL 或 SQLite)中逐行处理数据所带来的开销。向量化查询执行带来了远高于传统系统的 OLAP 查询性能。
可扩展
DuckDB 提供了一种灵活的扩展机制,允许定义新的数据类型、函数、文件格式和新的 SQL 语法。事实上,DuckDB 的许多关键功能,如对 Parquet 文件格式、JSON、时区,以及对 HTTP(S) 和 S3 协议的支持,都是作为扩展实现的。扩展同样可在 DuckDB Wasm 中工作。用户贡献的扩展可作为社区扩展使用。
免费
DuckDB 的开发始于主要开发人员在荷兰担任公职期间。我们认为,将我们的工作成果免费提供给荷兰或其他地区的任何人是我们的社会责任和义务。这就是为什么 DuckDB 在非常宽松的 MIT 许可证下发布,且项目的知识产权由 DuckDB 基金会持有。只要遵守我们的行为准则,我们欢迎任何人的贡献。
经过全面测试
虽然 DuckDB 最初是由一个研究小组创建的,但它从未打算成为一个研究原型。相反,它旨在成为一个稳定且成熟的数据库系统。为了实现这种稳定性,DuckDB 使用持续集成进行了密集测试。DuckDB 的测试套件目前包含数百万个查询,并包括从 SQLite、PostgreSQL 和 MonetDB 的测试套件中改编的查询。测试会在各种平台和编译器上重复进行。每个拉取请求都会根据完整的测试设置进行检查,只有通过后才能合并。
除了此测试套件之外,我们还运行各种测试来在重负载下压测 DuckDB。我们运行 TPC-H 和 TPC-DS 基准测试,并运行各种由多个客户端并行使用 DuckDB 的测试。
同行评审论文与学位论文
- 运行时可扩展解析器 (CIDR 2025)
- 固态硬盘时代下稳健的外部哈希聚合 (ICDE 2024)
- 这些行是为了排序而生的,而这正是我们要做的 (ICDE 2023)
- 在(几乎)没有统计信息的情况下进行连接顺序优化 (硕士论文, 2022)
- DuckDB-Wasm:Web 的快速分析处理 (VLDB 2022 演示)
- 数据科学的数据管理 - 迈向嵌入式分析 (CIDR 2020)
- DuckDB:一个可嵌入的分析型数据库 (SIGMOD 2019 演示)
使用 DuckDB 构建的项目
要了解使用 DuckDB 的项目,请访问 Awesome DuckDB 仓库。
站在巨人的肩膀上
DuckDB 使用了来自各种开源项目的一些组件,并从科学出版物中汲取灵感。我们对此深表感谢。以下是概述:
- 执行引擎:向量化执行引擎的灵感来自于 Peter Boncz、Marcin Zukowski 和 Niels Nes 的论文 MonetDB/X100: Hyper-Pipelining Query Execution。MonetDB/X100 后来演变为 Vectorwise (Actian Vector) 数据库系统。
- 优化器:DuckDB 的优化器灵感来自于 Guido Moerkotte 和 Thomas Neumann 的论文 Dynamic programming strikes back 以及 Thomas Neumann 和 Alfons Kemper 的 Unnesting Arbitrary Queries。
- 并发控制:我们的 MVCC 实现灵感来自于 Thomas Neumann、Tobias Mühlbauer 和 Alfons Kemper 的论文 Fast Serializable Multi-Version Concurrency Control for Main-Memory Database Systems。
- 二级索引:DuckDB 对二级索引的支持基于 Viktor Leis、Alfons Kemper 和 Thomas Neumann 的论文 The Adaptive Radix Tree: ARTful Indexing for Main-Memory Databases。
- SQL 窗口函数:DuckDB 的窗口函数实现使用了段树聚合(Segment Tree Aggregation),如 Viktor Leis、Kan Kundhikanjana、Alfons Kemper 和 Thomas Neumann 的论文 Efficient Processing of Window Functions in Analytical SQL Queries 中所述。
- SQL 不等式连接:DuckDB 的不等式连接实现使用了 IEJoin 算法,如 Zuhair Khayyat 等人的论文 Lightning Fast and Space Efficient Inequality Joins 中所述。
- 浮点数压缩:DuckDB 支持多种压缩浮点数值的算法:
- Chimp(由 Panagiotis Liakos, Katia Papakonstantinopoulou 和 Yannis Kotidis 提出)
- Patas(内部开发)
- ALP (adaptive lossless floating-point compression)(由 Azim Afroozeh, Leonard Kuffo 和 Peter Boncz 提出,他们也贡献了实现)
- SQL 解析器:我们使用了被重新封装为独立库的 PostgreSQL 解析器。转换为我们自己的解析树的过程灵感来自 Peloton。
- Shell:我们使用 SQLite shell 来操作 DuckDB。
- 正则表达式:DuckDB 使用 Google 的 RE2 正则表达式引擎。
- 字符串格式化:DuckDB 使用 fmt 字符串格式化库。
- UTF-8 处理:DuckDB 使用 utf8proc 库来检查和规范化 UTF-8。
- 排序规则与时间:DuckDB 使用 ICU 库来支持排序规则、时区和日历。
- 测试框架:DuckDB 使用 Catch2 单元测试框架。
- 测试用例:我们使用 来自 SQLite 的 SQL 逻辑测试 来测试 DuckDB。
- 结果验证:Manuel Rigger 使用他出色的 SQLancer 工具来验证 DuckDB 结果的正确性。
- 查询模糊测试:我们通过
sqlsmith扩展 使用 SQLsmith 来生成随机查询,以进行额外测试。 - JSON 解析器:我们使用 yyjson(一个用 ANSI C 编写的高性能 JSON 库)在 DuckDB 的 JSON 扩展中解析 JSON。
- 递归 CTE 中的
USING KEY:这是一个来自图宾根大学的创新想法,它允许将递归公用表表达式(CTE)中的中间结果视为键值字典,从而在性能和内存使用方面带来显著改进。参见论文 “How DuckDB isUSING KEYto Unlock Recursive Query Performance”。