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
  • 主要编程语言: C/C++

image

  • 支持多种数据格式的导入与导出:
  • csv / excel / json / parquet 等本地文件格式
  • http(s) / s3 等远程文件格式

开源情况

  • DuckDB 采用 较为宽松的 MIT 协议开源。

其作为荷兰 CWI 数据库组的一个项目,学术气息比较浓厚,项目的组织很有教科书的感觉,架构很清晰,所以非常适合阅读学习。
从 OSS Insight 拉个一个 Star 数对比,可以看到 DuckDB 发展非常迅速。

https://ossinsight.io/analyze/duckdb/duckdb#overview

image

DuckDB 主要特点

  • DuckDB是一个免费的、开源的、面向单机的(嵌入式/非分布式的)数据库管理系统,专为【数据分析】和【在线分析处理】而设计。

这意味着以下几点:

  • 它是免费的开源软件,因此任何人都可以使用和修改代码。
  • 它是面向单机的/嵌入式的。

这意味着DBMS(数据库管理系统)与使用它的应用程序在同一进程中运行。这使得它快速且易于使用。

  • 它针对【数据分析】和【OLAP】(在线分析处理)进行了优化,而不仅仅是像典型数据库那样只针对事务数据。

这意味着数据【按列】而不是【按行】组织以优化聚合和分析。

  • 它支持【标准SQL】,因此可以在数据上运行查询、聚合、连接和其他SQL函数。
  • 它在【进程中运行】,即在应用程序本身内运行,而不是作为单独的进程运行。这消除了进程间通信的开销。

SQLite一样,它是一个简单的基于文件的数据库。因此,不需要单独安装服务器。只需将库包含在应用程序中即可。

主要优点

  • 易于安装、部署和使用。没有需要配置的服务器,可在应用程序内部嵌入运行,这使得它易于集成到不同编程语言环境中。
  • 尽管它很简单,但DuckDB具有丰富的功能集。它支持完整的SQL标准、事务、二级索引,并且与流行的数据分析编程语言如 Python 和 R 集成良好。
  • 免费的,任何人都可以使用和修改它,这降低了开发人员和数据分析师采用它的门槛。
  • 兼容性很好,几乎无依赖性,甚至可在浏览器中运行。
  • 具有灵活的扩展机制,这对于直接从 CSV、JSON、Parquet、MySQL 或直接从 S3 读取数据特别重要,能够大大提高开发人员的体验。
  • 可提供数据超出内存限制但小于磁盘容量规模下的工作负载,这样分析工作可通过 "便宜"的硬件来完成。

回到顶部(Back to Top)

2 安装部署篇

  • DuckDB 安装部署过程相当简单,且支持多种部署模式:

image

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 查询。

  • 以管理员权限打开 PowerShell :

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>
  • 安装完成后,输入 psduckdb 即可使用

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

image

回到顶部(Back to Top)

3 工作原理与架构篇

数据库架构

image

  • 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 专章

  • 查看 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']);
  • 查看 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 |
  • 查询 parquet 文件的数据结构(定义)

-- 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 |
  • 将 parquet 文件的数据插入本地表

-- 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);
  • 创建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 命令:

INSTALL httpfs;

image

  • 要加载扩展以供使用,请使用SQL命令:

LOAD httpfs;

image

  • 凭据与配置

加载扩展后,设置凭证和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|
**/

image

参数管理

  • 查看参数

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 set threads=10;
  • 查看单个参数

D SELECT current_setting('threads') AS threads;
+---------+
| threads |
+---------+
| 10 |
+---------+

Pragma 扩展

  • PRAGMA 语句是DuckDBSQLite中采用的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 │
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐