0 序
DuckDB 是近期非常火的一款 AP 数据库,其独特的定位很有趣。甚至有数据库产品考虑将其纳入进来,作为分析能力的扩展。
考虑到项目中一个数据处理场景,就此调研一二。
- DuckDB 的爆火,也给所有盲目追逐“大数据”的技术人敲响了警钟:
- DuckDB 是一场复古的叛逆,也是一场属于单机的复仇。它告诉我们:在算力爆炸的今天,小,即是快;简,即是强。
- 我们被云厂商和大数据鼓吹者洗脑太久了,总觉得不搞个集群、不弄个微服务,架构就不够“高大上”。但技术的本质是解决问题,而不是制造复杂度。
DuckDB := 数据分析领域的 SQLLite (列式存储)
适合分析 / 列压缩 / 每一列在磁盘和内存中以分块数组的形式存储
内存局部性优化 / 读取查询速度更快
物联网/移动端/个人PC端/中小企业(TB级以下数据场景,尤其是数百GB级以下)
回到顶部(Back to Top)
1 概述:DuckDB
DuckDB 介绍
产品定位与产生背景
DuckDB 是一个诞生于2018年,开源免费的、面向嵌入式场景的、列式存储的、In-Process 的 OLAP 数据库。
- 产生背景:2019 年, SIGMOD 有一篇 Demo 论文介绍
DuckDB:an embedded analytical database。随着单机内存的变大,大部分 OLTP 数据库都能在内存中放得下,而很多 OLAP 也有在单机就能搞定的趋势。单台服务器的内存很容易达到 TB,加上 SSD,搞个几十甚至上百 TB 很容易。DuckDB 就是为了填补这个空白而生的。
- 定位:一款单机版/嵌入式分析型数据库 (数据分析领域的
SQLLite,但其底层是列式存储)
- Slogan : DuckDB 是一个分析式的 SQL 数据库管理系统
- https://github.com/duckdb/duckdb

- csv / excel / json / parquet 等本地文件格式
- http(s) / s3 等远程文件格式
开源情况
DuckDB 采用 较为宽松的 MIT 协议开源。
其作为荷兰 CWI 数据库组的一个项目,学术气息比较浓厚,项目的组织很有教科书的感觉,架构很清晰,所以非常适合阅读学习。
从 OSS Insight 拉个一个 Star 数对比,可以看到 DuckDB 发展非常迅速。
https://ossinsight.io/analyze/duckdb/duckdb#overview

DuckDB 主要特点
DuckDB是一个免费的、开源的、面向单机的(嵌入式/非分布式的)数据库管理系统,专为【数据分析】和【在线分析处理】而设计。
这意味着以下几点:
- 它是免费的开源软件,因此任何人都可以使用和修改代码。
- 它是面向单机的/嵌入式的。
这意味着DBMS(数据库管理系统)与使用它的应用程序在同一进程中运行。这使得它快速且易于使用。
- 它针对【数据分析】和【OLAP】(在线分析处理)进行了优化,而不仅仅是像典型数据库那样只针对事务数据。
这意味着数据【按列】而不是【按行】组织以优化聚合和分析。
- 它支持【标准SQL】,因此可以在数据上运行查询、聚合、连接和其他SQL函数。
- 它在【进程中运行】,即在应用程序本身内运行,而不是作为单独的进程运行。这消除了进程间通信的开销。
与SQLite一样,它是一个简单的、基于文件的数据库。因此,不需要单独安装服务器。只需将库包含在应用程序中即可。
主要优点
- 易于安装、部署和使用。没有需要配置的服务器,可在应用程序内部嵌入运行,这使得它易于集成到不同编程语言环境中。
- 尽管它很简单,但DuckDB具有丰富的功能集。它支持完整的SQL标准、事务、二级索引,并且与流行的数据分析编程语言如 Python 和 R 集成良好。
- 免费的,任何人都可以使用和修改它,这降低了开发人员和数据分析师采用它的门槛。
- 兼容性很好,几乎无依赖性,甚至可在浏览器中运行。
- 具有灵活的扩展机制,这对于直接从 CSV、JSON、Parquet、MySQL 或直接从 S3 读取数据特别重要,能够大大提高开发人员的体验。
- 可提供数据超出内存限制但小于磁盘容量规模下的工作负载,这样分析工作可通过 "便宜"的硬件来完成。
回到顶部(Back to Top)
2 安装部署篇
DuckDB 安装部署过程相当简单,且支持多种部署模式:

CLI / Python / Go / Java / Nodejs / C/C++ / R / Rust / ODBC
简言之,DuckDB 提供了非常简单的安装方法,可从官网 https://duckdb.org/install/ 直接下载安装解压即可使用。此外,DuckDB 还可以内置在多种开发语言中使用。
安装 CLI by Windows(Powershell)
DuckDB 可以通过 PSDuckDB 模块与 PowerShell 无缝集成,从而实现从 PowerShell 环境中高效执行分析性 SQL 查询。
|
PS C:\Windows\system32> Install-Module PSDuckDB 需要使用 NuGet 提供程序来继续操作 |
|
PowerShellGet 需要使用 NuGet 提供程序“2.8.5.201”或更高版本来与基于 NuGet 的存储库交互。必须在“C:\Program |
|
Files\PackageManagement\ProviderAssemblies”或“C:\Users\EDY\AppData\Local\PackageManagement\ProviderAssemblies”中提供 NuGet 提供程序。也可以通过运行 |
|
'Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force' 安装 NuGet 提供程序。是否要让 PowerShellGet 立即安装并导入 NuGet 提供程序? |
|
[Y] 是(Y) [N] 否(N) [S] 暂停(S) [?] 帮助 (默认值为“Y”): Y |
|
|
|
不受信任的存储库 |
|
你正在从不受信任的存储库安装模块。如果你信任该存储库,请通过运行 Set-PSRepository cmdlet 更改其 InstallationPolicy 值。是否确实要从“PSGallery”安装模块? |
|
[Y] 是(Y) [A] 全是(A) [N] 否(N) [L] 全否(L) [S] 暂停(S) [?] 帮助 (默认值为“N”): Y |
|
|
|
PS C:\Windows\system32> |
|
PS C:\Windows\system32> psduckdb |
|
Welcome to PSDuckDB! 01/02/2026 09:02:51 |
|
Connected to an in-memory database |
|
PSDuckDB: show databases; |
|
|
|
database_name |
|
------------- |
|
memory |
|
|
|
|
|
PSDuckDB: show tables |
|
PSDuckDB: select 1 as tmp_a |
|
|
|
tmp_a |
|
----- |
|
1 |
|
|
|
PSDuckDB: |
|
PSDuckDB: exit |
|
PS C:\Windows\system32> |
安装 CLI by Windows(解压即安装)
- 下载后解压 zip 安装包,双击打开
duckdb.exe 即可使用
https://duckdb.org/install/?platform=windows&environment=cli
D:\Program\DuckDB-CLI\duckdb.exe

回到顶部(Back to Top)
3 工作原理与架构篇
数据库架构

- DuckDB 数据库可分为多个组件:Parser、Logical Planner、Optimizer、Physical Planner、Execution Engine、Transaction and Storage Managers
Parser
- DuckDB SQL Parser 源自 Postgres SQL Parser。
Logical Planner
- 其包含了两个过程 binder、plan generator。前者是解析所有引用的 schema 中的对象(如 table 或 view)的表达式,将其与列名和类型匹配。后者将 binder 生成的 AST 转换为由基本 logical query 查询运算符组成的树,就得到了一颗 type-resolved logical query plan。
Optimizer
优化器部分,会采用多种优化手段对 logical query plan 进行优化,最终生成 physical plan。例如,其内置一组 rewrite rules 来简化 expression tree,例如执行公共子表达式消除和常量折叠。针对表关联,会使用动态规划进行 join order 的优化,针对复杂的 join graph 会 fallback 到贪心算法会消除所有的 subquery。
Execution Engine
DuckDB 最开始采用了基于 Pull-based 的 Vector Volcano 的执行引擎,后来切换到了 Push-based 的 pipelines 执行方法。DuckDB 采用了向量化计算来来加速计算,具有内部实现的多种类型的 vector 以及向量化的 operator。另外出于可移植性原因,没有采用 JIT,因为 JIT引擎依赖于大型编译器库(例如LLVM),具有额外的传递依赖。
Transactions
DuckDB 通过 MVCC 提供了 ACID 的特性,实现了HyPer专门针对混合OLAP OLTP系统定制的可串行化MVCC 变种 。该变种立即 in-place 更新数据,并将先前状态存储在单独的 undo buffer 中,以供并发事务和 abort 使用。
Persistent Storage
DuckDB 使用面向读取优化的 DataBlocks 存储布局(单个文件)。逻辑表被水平分区为 chunks of columns,并使用轻量级压缩方法压缩成 physical block 。每个块都带有每列的min/max 索引,以便快速确定它们是否与查询相关。此外,每个块还带有每列的轻量级索引,可以进一步限制扫描的值数量。
回到顶部(Back to Top)
4 使用指南篇
(本地)数据导入和导出
|
SELECT * FROM read_csv('input.csv'); |
|
COPY tbl TO 'output.csv' (HEADER, DELIMITER ','); |
|
SELECT |
|
size, parse_path(filename), content |
|
FROM read_text('test/sql/table_function/files/*.txt'); |
|
SELECT * FROM read_xlsx('test_excel.xlsx'); |
|
COPY tbl TO 'output.xlsx' WITH (FORMAT xlsx); |
|
SELECT * FROM read_json_auto('input.json'); |
|
# COPY (SELECT * FROM range(3) tbl(n)) TO 'output.json'; |
|
{"n":0} |
|
{"n":1} |
|
{"n":2} |
|
|
|
# COPY (SELECT * FROM range(3) tbl(n)) TO 'output.json' (ARRAY); |
|
[ |
|
{"n":0}, |
|
{"n":1}, |
|
{"n":2} |
|
] |
https://duckdb.org/docs/stable/guides/network_cloud_storage/http_import
|
SELECT * FROM read_parquet('input.parquet'); |
|
SELECT * FROM read_parquet('https://domain/path/to/file.parquet'); |
|
SELECT * FROM read_parquet('s3://{bucketName}/path/to/file.parquet'); |
|
COPY (SELECT * FROM tbl) TO 'output.parquet' (FORMAT parquet); |
|
SELECT * FROM read_parquet('input.parquet'); |
DuckDB 支持使用该协议。目前支持以下格式:file:
file:/some/path(host完全省略)
file:///some/path(空主)
file://localhost/some/path (localhost作为host)
请注意,以下格式不被支持,因为它们是非标准的:
file:some/relative/path(相对路径)
file://some/path(双斩路径)
此外,该协议目前不支持远程(非本地主机)主机。
Parquet 专章
|
D select * from read_parquet("D:\Program-Data\DuckDB\datasources\tb_demo.parquet") limit 10 |
|
col_0|col_1|col_2|col_3| |
|
-----+-----+-----+-----+ |
|
1| 2| 3| 4| |
|
5| 6| 7| 8| |
|
9| 10| 11| 12| |
|
13| 14| 15| 16| |
- 查询多个parquet文件的数据 (当数据结构(定义)一致时, 支持多个文件读.)
|
-- read 3 parquet files and treat them as a single table |
|
SELECT * FROM read_parquet(['file1.parquet', 'file2.parquet', 'file3.parquet']); |
| Wildcard |
Description |
* |
matches any number of any characters (including none) |
? |
matches any single character |
[abc] |
matches one character given in the bracket |
[a-z] |
matches one character from the range given in the bracket |
|
-- read all files that match the glob pattern |
|
SELECT * FROM read_parquet('test/*.parquet'); |
|
|
|
-- Read all parquet files from 2 specific folders |
|
SELECT * FROM read_parquet(['folder1/*.parquet','folder2/*.parquet']); |
文件路径 / 列名 / 列序 / 列类型 / 压缩算法(zstd / snappy / ...) / ...
|
D SELECT * FROM parquet_metadata("D:\Program-Data\DuckDB\datasources\tb_demo.parquet") |
|
file_name |row_group_id|row_group_num_rows|row_group_num_columns|row_group_bytes|column_id|file_offset|num_values|path_in_schema|type |stats_min|stats_max|stats_null_count|stats_distinct_count|stats_min_value|stats_max_value|compression|encodings |index_page_offset|dictionary_page_offset|data_page_offset|total_compressed_size|total_uncompressed_size|key_value_metadata|bloom_filter_offset|bloom_filter_length|min_is_exact|max_is_exact|row_group_compressed_bytes|geo_bbox|geo_types| |
|
--------------------------------------------------+------------+------------------+---------------------+---------------+---------+-----------+----------+--------------+-----+---------+---------+----------------+--------------------+---------------+---------------+-----------+--------------------------+-----------------+----------------------+----------------+---------------------+-----------------------+------------------+-------------------+-------------------+------------+------------+--------------------------+--------+---------+ |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet| 0| 4| 4| 476| 0| 0| 4|col_0 |INT64|1 |13 | 0| |1 |13 |SNAPPY |PLAIN, RLE, RLE_DICTIONARY| | 4| 46| 117| 119|{} | | |true |true | 1| |NULL | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet| 0| 4| 4| 476| 1| 0| 4|col_1 |INT64|2 |14 | 0| |2 |14 |SNAPPY |PLAIN, RLE, RLE_DICTIONARY| | 121| 163| 117| 119|{} | | |true |true | 1| |NULL | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet| 0| 4| 4| 476| 2| 0| 4|col_2 |INT64|3 |15 | 0| |3 |15 |SNAPPY |PLAIN, RLE, RLE_DICTIONARY| | 238| 280| 117| 119|{} | | |true |true | 1| |NULL | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet| 0| 4| 4| 476| 3| 0| 4|col_3 |INT64|4 |16 | 0| |4 |16 |SNAPPY |PLAIN, RLE, RLE_DICTIONARY| | 355| 397| 117| 119|{} | | |true |true | 1| |NULL | |
|
-- fetch the column names and column types |
|
D DESCRIBE SELECT * FROM "D:\Program-Data\DuckDB\datasources\tb_demo.parquet"; |
|
column_name|column_type|null|key|default|extra| |
|
-----------+-----------+----+---+-------+-----+ |
|
col_0 |BIGINT |YES | | | | |
|
col_1 |BIGINT |YES | | | | |
|
col_2 |BIGINT |YES | | | | |
|
col_3 |BIGINT |YES | | | | |
|
|
|
|
|
-- fetch the internal schema of a parquet file |
|
D SELECT * FROM parquet_schema("D:\Program-Data\DuckDB\datasources\tb_demo.parquet"); |
|
file_name |name |type |type_length|repetition_type|num_children|converted_type|scale|precision|field_id|logical_type|duckdb_type| |
|
--------------------------------------------------+------+-----+-----------+---------------+------------+--------------+-----+---------+--------+------------+-----------+ |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet|schema| | |REQUIRED | 4| | | | | | | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet|col_0 |INT64| |OPTIONAL | | | | | | |BIGINT | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet|col_1 |INT64| |OPTIONAL | | | | | | |BIGINT | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet|col_2 |INT64| |OPTIONAL | | | | | | |BIGINT | |
|
D:\Program-Data\DuckDB\datasources\tb_demo.parquet|col_3 |INT64| |OPTIONAL | | | | | | |BIGINT | |
|
-- insert the data from the parquet file in the table |
|
INSERT INTO people SELECT * FROM read_parquet('test.parquet'); |
|
|
|
-- create a table directly from a parquet file |
|
CREATE TABLE people AS SELECT * FROM read_parquet('test.parquet'); |
|
|
|
-- or |
|
COPY tbl FROM 'input.parquet' (FORMAT PARQUET); |
|
-- create a view over the parquet file |
|
CREATE VIEW people AS SELECT * FROM read_parquet('test.parquet'); |
|
|
|
-- query the parquet file |
|
SELECT * FROM people; |
(远程)网络与云存储
S3 Parquet Import
- 要从 S3 加载 Parquet 文件,需要 httpfs 扩展名。这可以通过SQL命令安装。这个程序只需要运行一次 INSTALL 命令:


加载扩展后,设置凭证和S3区域读取数据:httpfs
|
-- 创建 SECRET |
|
CREATE OR REPLACE SECRET s3_secret_qiniu ( -- or 持久化存储密钥: CREATE PERSISTENT SECRET s3_secret_qiniu ( |
|
TYPE s3 |
|
-- , PROVIDER config -- (可选配置项) 作用: 当遇到 s3: 协议的 http 文件时,默认使用此 secret |
|
, ENDPOINT 's3.cn-south-1.qiniucs.com' -- 's3.oss-cn-beijing.aliyuncs.com' (阿里云为例) |
|
, KEY_ID 'R534353545DrQp0ipYngKsey' -- 'AKIAIO3535355AMPLE' |
|
, SECRET '02xSD945454545gl4543535sD' -- 'wJalrXUtnF353535DENG/bPxRfiCYEXAMPLEKEY' |
|
|
|
-- , REGION 'cn-south-1' -- 如 'us-east-1' (可选配置项) |
|
-- , SCOPE 's3://{bucketName}' -- (可选配置项) |
|
); |
|
|
|
-- 查看 secret |
|
SELECT * FROM duckdb_secrets(); |
|
|
|
-- 删除 secret |
|
-- DROP SECRET s3_secret_qiniu; -- 或 删除持久化的密钥: DROP PERSISTENT SECRET s3_secret_qiniu; |
|
|
:注:默认情况下,会将持久化的密码信息(未加密)写入 ~/.duckdb/stored_secrets 目录。要更改秘密目录,请执行
|
SET secret_directory = 'path/to/my_secrets_dir'; |
- 查询远程对象存储中指定 parquet 文件的数据
|
-- 从s3查询 parquet 文件的数据 |
|
select * from read_parquet('s3://{bucketName}/dataset/tb_demo.parquet') |
|
|
|
/** |
|
col_0|col_1|col_2|col_3| |
|
-----+-----+-----+-----+ |
|
1| 2| 3| 4| |
|
5| 6| 7| 8| |
|
9| 10| 11| 12| |
|
13| 14| 15| 16| |
|
**/ |

参数管理
|
D select name,value from duckdb_settings(); |
|
name |value |
|
-------------------------------------------+----- |
|
Calendar |grego |
|
TimeZone |Asia/ |
|
access_mode |autom |
|
allocator_background_threads |false |
|
allocator_bulk_deallocation_flush_threshold|512.0 |
|
allocator_flush_threshold |128.0 |
|
allow_community_extensions |true |
|
allow_extensions_metadata_mismatch |false |
|
allow_persistent_secrets |true |
|
allow_unredacted_secrets |false |
|
allow_unsigned_extensions |false |
|
allowed_directories |[] |
|
allowed_paths |[] |
|
arrow_large_buffer_size |false |
|
arrow_lossless_conversion |false |
|
arrow_output_list_view |false |
|
... |
|
disabled_optimizers | |
|
duckdb_api |jdbc |
|
dynamic_or_filter_threshold |50 |
|
enable_curl_server_cert_verification |true |
|
... |
|
D SELECT current_setting('threads') AS threads; |
|
+---------+ |
|
| threads | |
|
+---------+ |
|
| 10 | |
|
+---------+ |
Pragma 扩展
PRAGMA 语句是DuckDB从SQLite中采用的SQL扩展。
PRAGMA语句可以以与常规SQL语句类似的方式发出。
PRAGMA命令可能会改变数据库引擎的内部状态,并可能影响引擎的后续执行或行为。
数据库信息
|
-- 数据库信息 |
|
D PRAGMA database_list; |
|
+------+------+---------------------------------------+ |
|
| seq | name | file | |
|
+------+------+---------------------------------------+ |
|
| 1080 | file | ...file.db | |
|
+------+------+---------------------------------------+ |
|
或: |
|
seq|name |file| |
|
---+------+----+ |
|
592|memory| | |
|
-- 数据库信息(大小) |
|
D CALL pragma_database_size(); |
|
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+ |
|
| database_name | database_size | block_size | total_blocks | used_blocks | free_blocks | wal_size | memory_usage | memory_limit | |
|
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+ |
|
| file | 512.0 KiB | 262144 | 2 | 2 | 0 | 0 bytes | 256.0 KiB | 25.0 GiB | |
|
+---------------+---------------+------------+--------------+-------------+-------------+----------+--------------+--------------+ |
查看表信息
|
-- 所有表信息 |
|
D PRAGMA show_tables; |
|
+------+ |
|
| name | |
|
+------+ |
|
| t1 | |
|
| t2 | |
|
| test | |
|
+------+ |
|
-- 表详细信息 |
|
D PRAGMA show_tables_expanded; |
|
+----------+--------+------+--------------+--------------------+-----------+ |
|
| database | schema | name | column_names | column_types | temporary | |
|
+----------+--------+------+--------------+--------------------+-----------+ |
|
| file | main | t1 | [a, b] | [INTEGER, INTEGER] | false | |
|
| file | main | t2 | [a, b] | [INTEGER, INTEGER] | false | |
|
| file | main | test | [i] | [INTEGER] | false | |
|
+----------+--------+------+--------------+--------------------+-----------+ |
|
-- 表结构 |
|
D PRAGMA table_info('t1'); |
|
+-----+------+---------+---------+------------+-------+ |
|
| cid | name | type | notnull | dflt_value | pk | |
|
+-----+------+---------+---------+------------+-------+ |
|
| 0 | a | INTEGER | false | | false | |
|
| 1 | b | INTEGER | false | | false | |
|
+-----+------+---------+---------+------------+-------+ |
查看函数信息
|
-- 函数信息 |
|
D PRAGMA functions; |
|
D PRAGMA functions; |
|
┌────────────┬─────────┬────────────────────────┬─────────┬─────────────┬──────────────┐ |
|
│ name │ type │ parameters │ varargs │ return_type │ side_effects │ |
|
│ varchar │ varchar │ varchar[] │ varchar │ |
所有评论(0)