一条在笔记本上就能复现的微基准:百万边图的单源可达性查询,用递归 CTE 写成普通 SQL,DuckDB v1.5.4 运行 4.90 秒,v2.0 预览版 0.12 秒,差距约 40 倍。这组数字来自 DuckDB 团队 8 月 17 日发布的 v2.0 预览公告。版本代号 Cyanoptera,取自分布于美洲西部的肉桂水鸭(Anas cyanoptera),计划今秋正式发布。自 3 月的 v1.5 以来,这个版本累计了超过 10000 个提交,涉及新的 SQL 解析器、新的默认存储格式、重构的 C API 和少量有意为之的破坏性变更。

这个版本的叙事主线官方写得很直白:去年是 lakehouse 之年,今年开启 DuckDB as a Server。十项变化从 SQL 层一路下探到存储引擎,以下逐项拆解。
服务器模式:Quack 协议与 CONNECT 语句
DuckDB 一直是进程内数据库,社区对 client/server 模式的请求持续多年,v2.0 正式补上。承担这个角色的是 quark 协议扩展(仓库 duckdb/duckdb-quack),实现 DuckDB 进程间的原生通信协议,今年 5 月以预览形式发布,v2.0 中转为稳定。
服务端一行命令开始监听:
CALL quack_serve(token = 'my_token');客户端通过 ATTACH 挂载远程实例,再用新的 CONNECT 语句把会话指向它:
ATTACH 'quack:server.example.com' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT count(*) FROM events; -- 在服务端执行,结果流回客户端
DISCONNECT;CONNECT 的作用范围不限于 Quack。remote pushdown 优化器(PR #22914)会把 SQL 直接推送到 PostgreSQL 或 MySQL 执行,避免整表经网络拉取:
CONNECT 'postgres://localhost/mydb';
SELECT count(*) FROM orders; -- 在 PostgreSQL 服务器上运行
DISCONNECT;官方在文中强调了一个长期被低估的事实:DuckDB 从第一天起就是带完整 MVCC 和事务隔离的事务型多连接数据库,单机单用户场景掩盖了这部分能力。client/server 模式放开后,这套机制开始在多租户、长驻进程场景发挥作用。配套的可观测性改造(metrics 层重构,PR #22799)也进入这个版本。Quack 协议预览发布后数周内,社区就出现了独立实现的第三方客户端。
VARIANT 类型转正
VARIANT 在 v1.5 引入,官方对它的定位是提速版 JSON:同 JSON 一样可以每行存储不同结构的数据,但底层并非文本格式。DuckDB 自动识别半结构化数据中的公共结构并做 shredded 处理,存储端压缩率高,查询端执行快,全程无需声明 schema,适合结构持续演化的实时日志摄取场景。
v2.0 把这条管线打通到端到端:从存储直接执行 shredded 数据(PR #20912)、提取谓词下推到扫描层(PR #22478)、Parquet 的 shredded 读写,以及 variant_* 函数族:
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 查询不做任何修改即可获得这些收益。
触发器
这是社区呼声最高的功能之一。v2.0 的实现覆盖 BEFORE/AFTER 触发、FOR EACH ROW 与 FOR EACH STATEMENT、通过 REFERENCING OLD/NEW TABLE 访问转换表、同一事件挂多个触发器、触发表上的 RETURNING,以及 DROP TRIGGER。典型的审计表场景:
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;官方也打算在后续内部功能中复用触发器机制,它同时完整暴露在 SQL 层,开发者可以直接基于它构建自己的逻辑。
SQL 方言的六处增量
NEAREST 连接(PR #24137)把 top-k 相似度检索写成 join 子句,面向向量与嵌入 workload:
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(PR #21634 等)允许把 INSERT/UPDATE/DELETE/COPY 当作管道步骤使用:
WITH moved AS MATERIALIZED (
DELETE FROM staging RETURNING *
)
INSERT INTO archive SELECT * FROM moved;嵌套 schema(PR #23492)支持库内的层级命名空间,可以 CREATE SCHEMA finance.reports 再在其中建表。变量语法(PR #21194)用 $x 替代 getvariable() 调用,表达式允许的位置都能写。JSON 修改函数 json_set、json_insert、json_replace、json_remove(PR #23786)补上了原地修改 JSON 文档的能力。递归 CTE 配合 USING KEY 聚合(PR #19481)让纯 SQL 可以表达迭代算法。此外还有 SQL 标准的 FETCH FIRST n ROWS ONLY、OVERLAY()、GROUP BY 中的 UNNEST,以及 MERGE 和 UPDATE ... FROM 对多行匹配的明确语义(PR #24058)。
异步 I/O 与引擎提速
对象存储是 DuckDB 的主要数据来源,同步 I/O 一直是远程读取并行度的上限。v2.0 在引擎内全面引入异步 I/O:Parquet 支持先行(PR #23662),CSV(#23961)和自有文件格式(#24654)跟进,附带异步 Parquet 写入(#23283)和新的 MMAP、DIRECT_IO 模式(#22988)。I/O 层与查询处理层解耦后各自独立扩展并行度,网络存储上的查询收益最大,本地存储略有改善。
通用查询提速来自多个优化器改进:部分聚合下推到 join 之下(#22572)、冗余聚合复用(#24543)、递归 CTE 引擎重写(#22211)、聚合内存不足时自动落盘(#24499)、Windows CLI 多线程结果物化提速约 2.2 倍(#24036)。开头那个 40 倍的递归查询数字,出处正是重写后的递归 CTE 引擎。
Row-group 剪枝的范围大幅扩展:min-max 索引(zone maps)和 Parquet Bloom filter 现在能对 struct、list、decimal、UUID、IN 过滤乃至函数谓词生效:
-- 以下查询现在跳过 row group,不再全量扫描
SELECT * FROM logs WHERE contains(message, 'ERROR');
SELECT * FROM t WHERE substr(code, 1, 3) = 'NL-';查询计划同时具备分区感知能力(PR #22336)。对 DuckLake、Iceberg 和 S3 上 Hive 分区 Parquet 这类天然分区的数据集,规划器可以直接跳过无关分区;分区写入侧也做了重构(#22225、#22620)。
存储格式 v2.0.0 与自研解析器
默认存储格式版本升到 v2.0.0(PR #22875)。头条变化是 ART 索引纳入 buffer manager 管理(#21458、#23605):索引不再常驻内存,带大索引的表即时打开,索引页按需换入。列元数据改为惰性加载(#22333),宽表打开更快;DICT_FSST 字符串压缩默认启用(#23733);删除记录紧凑存储(#24336);读取时的损坏校验加强。带大索引和宽表的数据库打开更快,内存占用显著降低。
SQL 解析器方面,DuckDB 此前一直使用 PostgreSQL 派生的解析器,v2.0 换成自研的、可扩展的 PEG 解析器(PR #22194)。扩展可以挂接语法本身,后续会出现暴露全新 SQL 语法的扩展;错误信息带精确源位置;并出现第一个方言兼容模式:
SET dialect_compatibility_mode = 'spark';官方承诺新旧解析器行为兼容,用户不应察觉差异,察觉了即属 bug。
时区、日历和排序规则不再依赖 ICU 库。icu 扩展改为自行实现这三类能力(PR #24463、#24403),时区数据直接取自 IANA 数据库并压缩到约 45KB。MacBook 上的实测数据:2500 万行时间戳转换到 Paris 时区从 0.24 秒降到 0.11 秒(2.2 倍),500 万行德语排序规则过滤从 0.15 秒降到 0.06 秒(2.6 倍)。
扩展生态:稳定 C API 与自托管仓库
多数现有扩展基于不稳定的 C++ API 构建,每次 DuckDB 发版都需要重定向重编译,社区扩展会随作者停更而静默失效。v2.0 把稳定 C API 的覆盖面扩展到扩展开发的全流程:API 从声明式、版本化的规范生成(PR #24135),duckdb.h、duckdb_extension.h 和扩展 ABI 中的每个函数都记录在 api_spec/ 目录的 YAML 中,CI 校验头文件与规范一致,API 与 ABI 不再漂移。配套改进包括统一符号版本(#24435)、自定义分配器(#23945)和 C API 扩展静态链接进应用(#22251)。基于稳定 C API 编译的扩展二进制可以跨 DuckDB 版本持续工作,一次编写、一次构建、一次发布。
分发侧的变化是自定义扩展仓库(PR #24777,开发中)。组织可以自建签名扩展源,DuckDB 以 RSA 公钥钉住仓库信任,创建时打印每个密钥的 SHA-256 指纹供带外核对;不信任网络时可以直接传入公钥,完全离线建仓:
SET allow_extension_repositories = 'allowed';
CREATE EXTENSION REPOSITORY my_repo FROM 'https://extensions.example.org';
INSTALL my_ext FROM my_repo;
LOAD my_repo/my_ext;仓库定义支持重启后保留、多密钥轮换,可随时通过 duckdb_extension_repositories() 表函数审计,用 DROP EXTENSION REPOSITORY 移除。
升级影响与时间表
破坏性变更集中在两处:新默认存储格式,以及 lambda 语法迁移收尾,官方承诺在正式发布公告中详细说明。预览构建已包含上述大部分特性,可以安装验证;出问题走 GitHub issue tracker。
对存量用户,值得提前评估的点有三类:依赖旧解析器边缘行为的 SQL、直接读取数据库文件的外部工具链(存储格式升版后旧版本无法读取新库)、以及自行编译维护的 C++ 扩展(迁移到稳定 C API 后不再随版本返工)。递归 CTE 查询、网络存储读取、带大索引宽表这三类 workload 的收益最直接。
组织层面还有一个动向:DuckDB Foundation 将于今秋设立利益相关方顾问委员会,对 DuckDB、DuckLake 和 Quack 三个项目的路线图提供输入。
参考:A Preview of DuckDB v2.0(DuckDB 官方博客,Mark Raasveldt、Hannes Mühleisen,2026-08-17)