DuckDB v2.0 预览
摘要
DuckDB v2.0,代号 Cyanoptera,预览了新功能,包括服务器模式、触发器、VARIANT 类型、异步 I/O 和新的 SQL 解析器,将于今年秋季发布。
暂无内容
查看缓存全文
缓存时间: 2026/08/17 15:51
# DuckDB v2.0 预览
来源:https://duckdb.org/2026/08/17/duckdb-20-highlights
*简而言之:DuckDB v2.0 将于今年秋季发布。本文我们将预览其主打功能:DuckDB 服务器模式、触发器、VARIANT 类型、异步 I/O、全新 SQL 解析器、新存储格式等众多更新。*
DuckDB v2.0 将以“Cyanoptera”命名,灵感来源于一种羽毛呈现独特红褐色的鸭类——肉桂翅鸭(https://en.wikipedia.org/wiki/Cinnamon_teal)*(Anas cyanoptera)*,这种鸟类主要分布在美洲西部。我们谨慎对待版本大升级,这不仅是形式上的改变:v2.0 带来了全新的 SQL 解析器、默认存储格式升级、重构的 C API,以及经过精心挑选的少量破坏性变更。但最重要的是,这是一个功能发布版本,基于自 3 月发布 v1.5 以来超过 10,000 次代码提交构建。如果说去年是“湖仓一体”之年,那么本次发布标志着“DuckDB 服务器化”之年的开端。
如果您更倾向于观看而非阅读,我们曾在 DuckCon #7(https://www.youtube.com/watch?v=iFPKNQu0FtE)的“State of the Duck”演讲中预览过许多功能。DuckDB 发展迅速,本文仅能涵盖其中一小部分变更。将所有新功能浓缩成简短清单总是一场取舍的博弈——是的,我们知道这本质上又是一篇“十大新功能,第八项让你惊叹”的列表文。我们并不钟情于这种形式,但它确实有效,因此接下来我们将从 SQL 层功能开始,逐步深入引擎核心。
## 1. DuckDB 服务器化:Quack 与 `CONNECT` (https://duckdb.org/2026/08/17/duckdb-20-highlights#1-duckdb-as-a-server-quack-and-connect)
DuckDB 自诞生以来一直作为进程内数据库运行。但用户们持续不断地向我们请求客户端/服务器模式,我们最终让步了。`quack` 扩展(https://github.com/duckdb/duckdb-quack)实现了 DuckDB 与其他 DuckDB 实例通信的原生协议。它在 DuckCon #7 之前以预览版发布(https://duckdb.org/2026/05/12/quack-remote-protocol.html),将在 v2.0 中转为稳定版,这也是 DuckDB 未来发展方向的重要一环:任何 DuckDB 进程都可以通过网络提供其数据库服务,其他 DuckDB 实例可以挂载并使用新的 `CONNECT` 语句将查询路由到该服务。例如:
#### DuckDB 服务器 (https://duckdb.org/2026/08/17/duckdb-20-highlights#duckdb-server)
```sql
CALL quack_serve(token = 'my_token');
```
*quack:*
#### DuckDB 客户端 (https://duckdb.org/2026/08/17/duckdb-20-highlights#duckdb-client)
```sql
ATTACH 'quack:server.example.com' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT count(*) FROM events; -- 在服务器上执行,结果流式返回
DISCONNECT;
```
`CONNECT` 取代了我们在 Quack 首次亮相时展示的 `remote.query($$...$$)` 变通方案——我们审视了那个语法后认为:不行,这不该是最终方案。而且 `CONNECT` 并不局限于 Quack:它可以将你的会话指向任何支持该协议的远程数据库,新的远程下推优化器(#22914 (https://github.com/duckdb/duckdb/pull/22914))可以直接将 SQL 下发到 PostgreSQL 和 MySQL,而非将整个表拉取到本地:
```sql
CONNECT 'postgres://localhost/mydb';
SELECT count(*) FROM orders; -- 在 PostgreSQL 服务器上运行
DISCONNECT;
```
如果你过去使用过分析型系统,可能会认为 DuckDB 无法处理事务负载。但 DuckDB 自始至终被构建为一个支持完整 MVCC 和事务隔离的多连接事务型数据库。大多数用户只是在单用户场景下从未需要过此功能。事实证明 DuckDB 处理事务的能力很强:在不少负载下,其性能足以与 PostgreSQL 等通用数据库竞争,而客户端/服务器模式终于让这些机制在多租户、长期运行的部署中大放异彩。
长期运行 DuckDB 也带来了新的挑战,因此 v2.0 强化了指标、日志和可观测性(例如 #22799 (https://github.com/duckdb/duckdb/pull/22799) 中重构的指标层),让你可以直观地了解 DuckDB 实例的实际运行情况。甚至在预览版发布几周内,社区就已经有人为 Quack 协议构建了独立客户端。我们原以为是在扩展 DuckDB 与其他 DuckDB 通信的能力;而世界却说:不不不,然后自己造了客户端。谁能想到呢。
## 2. `VARIANT` 类型成为一等公民 (https://duckdb.org/2026/08/17/duckdb-20-highlights#2-variant-becomes-a-first-class-citizen)
`VARIANT` 类型随 DuckDB v1.5(https://duckdb.org/2026/03/09/announcing-duckdb-150.html)一同发布,可以将其理解为“增强版 JSON”。基本上,可以想象 JSON 变得非常快。与 JSON 类似,`VARIANT` 列可以在每一行存储不同结构的数据。不同于 JSON 的是,它不是一种文本格式:DuckDB 会自动检测半结构化数据中隐藏的通用结构并将其“解构”,使其在存储中压缩良好且在查询中快速执行,无需你预先声明模式。
这使得 `VARIANT` 非常适合实时日志摄取场景——其中 JSON 风格的记录流共享结构但随时间演进。在 v2.0 中,这一流水线实现了端到端工作:直接从存储执行解构(#20912 (https://github.com/duckdb/duckdb/pull/20912))、提取下推至扫描操作(#22478 (https://github.com/duckdb/duckdb/pull/22478))、对 Parquet 格式支持解构的 `VARIANT` 读写,以及一系列 `variant_*` 函数:
```sql
CREATE TABLE events (payload VARIANT);
INSERT INTO events VALUES ('{"user": {"id": 42, "tags": ["a", "b"]}}'::JSON::VARIANT);
SELECT variant_type(payload), variant_keys(payload) FROM events;
SELECT * FROM events WHERE variant_contains(payload, {'user': {'id': 42}}::VARIANT);
```
更长远来看(可能在 v2.0 发布后不久,但请勿强求),我们计划用 `VARIANT` 作为底层支持 `JSON` 类型,这样现有的 JSON 负载无需修改任何查询即可获得所有这些优势。
## 3. 触发器 (https://duckdb.org/2026/08/17/duckdb-20-highlights#3-triggers)
触发器是一项长期存在的功能请求,DuckDB v2.0 完整交付:`BEFORE` 和 `AFTER` 触发器、`FOR EACH ROW` 和 `FOR EACH STATEMENT`、通过 `REFERENCING OLD/NEW TABLE` 引用转换表、每个事件多个触发器、对触发表的 `RETURNING` 支持以及 `DROP TRIGGER`。
经典用例是审计表:系统中发生某个事件,触发器记录变更内容。例如:
```sql
CREATE TABLE target (id INTEGER, val INTEGER);
CREATE TABLE audit (id INTEGER, old_val INTEGER, new_val INTEGER);
CREATE TRIGGER trg_audit AFTER UPDATE ON target
REFERENCING OLD TABLE AS o NEW TABLE AS n
FOR EACH STATEMENT
INSERT INTO audit SELECT n.id, o.val, n.val FROM o JOIN n ON o.id = n.id;
INSERT INTO target VALUES (1, 10), (2, 20);
UPDATE target SET val = val * 10 WHERE id <= 2;
SELECT * FROM audit;
```
| id | old_val | new_val |
|----|---------|---------|
| 1 | 10 | 100 |
| 2 | 20 | 200 |
触发器与长期运行的 DuckDB 服务天然契合,我们也计划在内部使用它们来构建一些即将推出的功能。它们在 SQL 层完全暴露,你可以用它们构建自己的酷炫应用。
## 4. SQL 方言扩展 (https://duckdb.org/2026/08/17/duckdb-20-highlights#4-sql-dialect-additions)
一如既往,DuckDB 的 SQL 方言持续扩展。以下是本次发布周期中的一些亮点:
**`NEAREST` 连接**(#24137 (https://github.com/duckdb/duckdb/pull/24137))将 Top-K 相似性搜索变为连接子句,适用于向量和嵌入工作负载:
```sql
SELECT q.user_id, t.product_id
FROM users q
INNER JOIN products t
APPROX NEAREST 2 BY SIMILARITY array_cosine_similarity(q.embedding, t.embedding);
```
**CTE 中的 DML**(#21634 (https://github.com/duckdb/duckdb/pull/21634)、#21997 (https://github.com/duckdb/duckdb/pull/21997)、#24217 (https://github.com/duckdb/duckdb/pull/24217))允许将 `INSERT`、`UPDATE`、`DELETE` 和 `COPY` 作为流水线步骤:
```sql
WITH moved AS MATERIALIZED (
DELETE FROM staging RETURNING *
)
INSERT INTO archive
SELECT * FROM moved;
```
**嵌套模式**(#23492 (https://github.com/duckdb/duckdb/pull/23492)、#24222 (https://github.com/duckdb/duckdb/pull/24222))允许模式内嵌模式:
```sql
CREATE SCHEMA finance;
CREATE SCHEMA finance.reports;
CREATE TABLE finance.reports.q3 (revenue DECIMAL);
```
新的**变量语法**(#21194 (https://github.com/duckdb/duckdb/pull/21194))允许在任何允许表达式的地方编写 `$x`,不再需要 `getvariable(...)` 的繁琐写法(https://duckdb.org/docs/current/sql/functions/utility.html#getvariablevariable_name):
```sql
SET VARIABLE threshold = 100;
SELECT * FROM orders WHERE amount > $threshold;
```
**JSON 修改函数** `json_set`、`json_insert`、`json_replace` 和 `json_remove`(#23786 (https://github.com/duckdb/duckdb/pull/23786))终于允许原地修改 JSON 文档:
```sql
SELECT json_set('{"a":1}', '$.b', '2');
```
*json_set('{"a":1}', '$.b', '2')*
`{"a":1,"b":2}`
以及带 **`USING KEY`** 聚合(https://duckdb.org/2025/05/23/using-key.html)的**递归 CTE**(#19481 (https://github.com/duckdb/duckdb/pull/19481))支持在纯 SQL 中实现迭代算法,其后端是下文描述的重写后的递归 CTE 引擎:
```sql
WITH RECURSIVE tbl(a, b) USING KEY (a, avg(b)) AS (
SELECT 1, 5
UNION
SELECT a, b - 1 FROM tbl WHERE b > 0
)
TABLE tbl;
```
| a | b |
|---|-----|
| 1 | 2.5 |
更多新增功能包括:SQL 标准的 `FETCH FIRST 2 ROWS ONLY`(#23533 (https://github.com/duckdb/duckdb/pull/23533))、`OVERLAY()`(#22456 (https://github.com/duckdb/duckdb/pull/22456))、在 `GROUP BY` 中使用 `UNNEST`(#23644 (https://github.com/duckdb/duckdb/pull/23644)),以及为多匹配行定义良好的 `MERGE`/`UPDATE ... FROM` 语义(#24058 (https://github.com/duckdb/duckdb/pull/24058))。
## 5. 异步 I/O (https://duckdb.org/2026/08/17/duckdb-20-highlights#5-asynchronous-io)
与 S3 等对象存储交互是 DuckDB 体验的核心:数据总得有个来源,而它通常存储在对象存储中。DuckDB 很早就能够并行读取对象存储,但同步访问限制了其最大速度。DuckDB v2.0 在整个引擎中引入了异步 I/O。我们在一篇专门的博客文章(https://duckdb.org/2026/07/31/asynchronous-io.html)中详细描述了这一设计。
得益于异步访问,I/O 层现在可以独立于查询处理层进行扩展,这意味着远程读取可以实现更高的并行度,网络存储上的查询速度大幅提升。Parquet 支持先行实现(#23662 (https://github.com/duckdb/duckdb/pull/23662)),CSV(#23961 (https://github.com/duckdb/duckdb/pull/23961))和 DuckDB 自身文件格式(#24654 (https://github.com/duckdb/duckdb/pull/24654))紧随其后,还包括异步 Parquet 写入(#23283 (https://github.com/duckdb/duckdb/pull/23283))和新的 `MMAP` 与 `DIRECT_IO` 模式(#22988 (https://github.com/duckdb/duckdb/pull/22988))。本地存储也有一定改善,但网络存储才是你将看到巨大提升的地方。
## 6. 全面更快的查询 (https://duckdb.org/2026/08/17/duckdb-20-highlights#6-faster-queries-across-the-board)
与每次发布一样,大量工作投入到了让现有查询无需任何改动即可运行得更快。列举一些亮点:部分聚合现在被下推到连接之下(#22572 (https://github.com/duckdb/duckdb/pull/22572)),冗余聚合被复用(#24543 (https://github.com/duckdb/duckdb/pull/24543)),递归 CTE 引擎已被重写(#22211 (https://github.com/duckdb/duckdb/pull/22211)),聚合在超出内存时会溢出到磁盘(#24499 (https://github.com/duckdb/duckdb/pull/24499)),Windows 命令行版本在多线程结果物化上速度提升了约 2.2 倍(#24036 (https://github.com/duckdb/duckdb/pull/24036))。
还能快多少?这是一个可以在笔记本电脑上运行的微基准测试:使用普通递归 CTE(https://duckdb.org/docs/current/sql/query_syntax/with.html)计算包含一百万条边的图的单源可达性。
```sql
CREATE TABLE edges AS
SELECT (range % 100_000)::INTEGER AS src, ((range * 13 + 7) % 100_000)::INTEGER AS dst
FROM range(1_000_000);
WITH RECURSIVE reachable(node) AS (
SELECT 0
UNION
SELECT dst FROM edges, reachable WHERE src = node
)
SELECT count(*) FROM reachable;
```
| 版本 | 运行时间 |
|------|----------|
| DuckDB v1.5.4 | 4.90 秒 |
| DuckDB v2.0(预览版) | 0.12 秒 |
如你所见,对于相同的递归查询,DuckDB v2.0 快了大约 40 倍(!)。行组裁剪得到了大幅扩展:最小-最大索引(区域映射)(https://duckdb.org/docs/current/sql/indexes.html#min-max-index-zonemap%20%}) 和 Parquet 布隆过滤器(https://duckdb.org/2025/03/07/parquet-bloom-filters-in-duckdb.html)现在可以跳过结构体、列表、十进制数、UUID、`IN` 过滤器甚至函数谓词的数据:
```sql
-- 这些现在可以裁剪行组,而不是扫描它们:
SELECT * FROM logs WHERE contains(message, 'ERROR');
SELECT * FROM t WHERE substr(code, 1, 3) = 'NL-';
SELECT * FROM 'data/*.parquet' WHERE id IN (1, 5, 9);
```
查询规划还实现了**分区感知**(#22336 (https://github.com/duckdb/duckdb/pull/22336))。湖仓格式(https://duckdb.org/docs/current/lakehouse_formats.html)(DuckLake、Iceberg 以及 S3 上的纯 Hive 分区 Parquet)都是分区的,利用分区往往是扫描整个数据集与跳过大部分数据之间的区别。在 v2.0 中,规划器和优化器充分利用了现有分区,分区写入也得到了重构(#22225 (https://github.com/duckdb/duckdb/pull/22225)、#22620 (https://github.com/duckdb/duckdb/pull/22620))。
## 7. 存储格式 v2.0 (https://duckdb.org/2026/08/17/duckdb-20-highlights#7-storage-format-v20)
DuckDB v2.0 将默认存储格式版本 (https://duckdb.org/docs/current/internals/storage.html) 升级至 v2.0.0(#22875 (https://github.com/duckdb/duckdb/pull/22875))。主要变更是缓冲区管理的 ART 索引(#21458 (https://github.com/duckdb/duckdb/pull/21458)、#23605 (https://github.com/duckdb/duckdb/pull/23605)):索引不再固定在内存中,这意味着带有大索引的表可以瞬间打开,其索引按需加载。列元数据现在是延迟加载的(#22333 (https://github.com/duckdb/duckdb/pull/22333)),因此宽表的打开速度也更快。默认启用 `DICT_FSST` 字符串压缩方法(#23733 (https://github.com/duckdb/duckdb/pull/23733)),删除操作以紧凑方式存储(#24336 (https://github.com/duckdb/duckdb/pull/24336)),存储层在读取时执行更强的损坏验证。简而言之:拥有大索引和宽表的数据库打开更快,内存占用也大幅减少。
## 8. 全新的 SQL 解析器 (https://duckdb.org/2026/08/17/duckdb-20-highlights#8-a-brand-new-sql-parser)
DuckDB 一直以使用源自 PostgreSQL 的解析器而闻名。我们决定是时候改变了:v2.0 带来了我们自己的现代、可扩展的、基于 PEG 的解析器(#22194 (https://github.com/duckdb/duckdb/pull/22194)),这个想法最初在我们 2024 年关于运行时可扩展解析器(https://duckdb.org/2024/11/22/runtime-extensible-parsers.html)的文章中探讨过。
这一变化与扩展生态系统紧密相连:扩展现在可以钩入语法本身,因此可以期待暴露全新 SQL 语法的扩展。它还带来了更友好的错误信息和精确的源码位置,以及首个方言兼容模式:
```sql
SET dialect_compatibility_mode = 'spark';
```
由于我们设计时确保了与旧解析器的兼容性,你应该不会注意到解析器切换带来的任何变化。如果你确实注意到了差异,请提交 issue。
## 9. 无需 ICU 的时区、日历和排序规则 (https://duckdb.org/2026/08/17/duckdb-20-highlights#9-timezones-calendars-and-collations-without-icu)
DuckDB 中的时区感知时间戳、日历和排序规则一直依赖 ICU 库。
相似文章
DuckDB v2.0 预览
本文预览了即将发布的 DuckDB v2.0 功能,包括服务器模式、新的 SQL 解析器和存储格式,标志着数据库系统的重要更新。
DuckDB 1.5.2 – 可在笔记本、服务器、浏览器中运行的 SQL 数据库
DuckDB 1.5.2 带来生产就绪的 DuckLake v1.0 湖仓格式、Iceberg 扩展改进、Jepsen 测试,以及支持文件存储的全新浏览器 Shell。
DuckDB 中的异步 I/O:工作、线程、工作
DuckDB 在 v2.0 中为 Parquet 和 CSV 文件引入异步 I/O,通过避免在数据获取期间阻塞工作线程来提升远程存储查询性能。
Quack:DuckDB 的客户端-服务器协议
DuckDB 引入了名为“Quack”的全新客户端-服务器协议,使得 DuckDB 实例能够通过 HTTP 进行通信,在支持并发写入和远程访问的同时,保持简单性与高性能。
DuckDB: it's not quack science
DuckDB是一个开源嵌入式分析型数据库,支持直接查询文件、嵌入应用,并提供友好的SQL扩展,在数据分析场景下比传统Unix管道更高效。