为什么 2.0 不是又一次功能堆叠
DuckDB 1.0 的关键词是稳定与向后兼容,1.5 把 Quack 协议、VARIANT、Agent 可观测性铺了底。而 2.0 LTS(代号 “Cinnamon Teal”,取自美洲西部的彩头鸭 Spatula cyanoptera)自 1.5 以来累计近 2 万个 commit,官方把它定位成功能发布,不是修补。
判断一篇版本解读有没有"深度",标准不该是"列了多少新特性",而是能不能说清每个特性改变了数据库的哪一层边界。2.0 真正质变的点,是把 DuckDB 从一个进程内嵌的分析引擎,推成了一个可以同时以嵌入、服务、远程三种形态运行的数据库:
- 服务化:Quack 协议 +
CONNECT,任意 DuckDB 进程可把库服务出去,还能连 PostgreSQL/MySQL 并做远程下推 - 异步 I/O:读层与查询层解耦,对象存储(
s3://)上的查询吞吐显著提升 - VARIANT 成头等公民:半结构化数据"shredding"后列式执行,JSON 性能直追原生列
- 可插拔解析器:内置 PEG 解析器,扩展能自定义 SQL 语法
- 触发器:
CREATE TRIGGER补齐审计表与物化视图增量维护的最后一块 - Agent 模式:CLI 检测到被 AI 编程代理驱动时自动切紧凑输出,TPC-H 实测减少 59% 输出
下面逐层拆解,代码均用 DuckDB 2.0.0 CLI 实测(非 1.5)。
1. 服务化:Quack 协议与 CONNECT
DuckDB 从第一天起就是进程内嵌数据库。1.5.3 引入 Quack 协议后,2.0 把它做成了打磨过的一等能力——任何 DuckDB 进程都能用 quack_serve 把库服务出去,别的进程用 ATTACH ... 'quack:localhost' 连上。
服务端(起服务 + 写入):
CALL quack_serve (token = 'my_token');
CREATE TABLE events (date DATE, name VARCHAR);
INSERT INTO events VALUES ('2027-04-07', 'DuckCon #8');
客户端(连接 + 切换 + 查询):
ATTACH 'quack:localhost' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT * FROM events;
| date | name |
|---|---|
| 2027-04-07 | DuckCon #8 |
安全注意:默认只监听 localhost。跨机器访问应保持服务端在 localhost、前置 TLS 终结反向代理;
allow_other_hostname仅供测试。
CONNECT 的价值不止于 Quack——它能指向任何支持该协议的远程库,包括 PostgreSQL 和 MySQL。对这些目标,2.0 的远程下推优化器会把查询直接推到远端执行,而不是把整张表拉过网络回来再算。这一步把 DuckDB 从"本地分析引擎"扩展成"能跨数据源做联邦查询"的角色,是 SaaS 多租户、混合数据场景的关键一环(延伸阅读:DuckDB v2.0 Server Mode 架构)。
CLI 里还有快捷入口:duckdb -serve 起服务、duckdb -connect 直连,省得手写 quack_serve。
2. 性能:异步 I/O
2.0 给 Parquet、CSV 以及 DuckDB 原生存储格式引入异步 I/O:I/O 层不再与查询处理层绑定,读操作在后台发起、多个请求并行在途。对本地盘是"锦上添花",对 s3:// 这类高延迟对象存储则是数量级改善——网络吞吐利用率更高、查询运行时间更短。
设计细节官方单独发了一篇博客讲清(含启用前后吞吐对比曲线),这里不展开。记住一句:读远端大文件时,2.0 的异步 I/O 是你白拿的性能红利。
3. VARIANT 成为头等公民
VARIANT 在 1.5 已引入,但 2.0 把它完整集成了:存的是 schema-less 半结构化数据(典型如 JSON),却保留列式执行的性能——可理解为"加强版 JSON"。每一行可以是不同形状的数据,但它不是文本格式。
核心机制是 shredding(解构):2.0 检测数据中的公共结构,把它"打散"成列式存储,于是既能高压缩、又能快速执行。流水线端到端打通——从解构后的存储执行,到 Parquet 的 VARIANT 读写,再到一族 variant_* 函数:
CREATE TABLE events (payload VARIANT);
INSERT INTO events VALUES
('{"user": {"id": 42, "tags": ["a","b"]}}'::JSON::VARIANT),
('{"event":"click","x":10,"y":20}'::JSON::VARIANT);
SELECT variant_type(payload) AS t, variant_keys(payload) AS k FROM events;
| t | k |
|---|---|
| OBJECT | [user] |
| OBJECT | [event, x, y] |
对做事件流、埋点、半结构化日志分析的人,这一条意味着:不用再为 JSON 的灵活性牺牲列式性能。
4. Agent 模式:为 AI 编程代理而设计
这是 2.0 里最"时代"的功能。当 CLI 检测到被 Claude Code、Codex、Cursor 这类编程代理驱动时,自动切到 Agent 模式:
- 输出紧凑 Markdown 表(而非填充格子的盒子)
- 明确标注结果是否被截断
- 提前中止失控查询
- 报错用 JSON
- 长查询先报告预估成本,让代理决定是否等
官方在 TPC-H 上实测:Agent 模式让模型要读的 DuckDB 输出减少 59%。手动测试可用 -agent,让代理关闭用 -no-agent:
duckdb -agent :memory: "SELECT 42 AS x;"
实测输出是紧凑形式:
| x:INTEGER |
|---|
| 42 |
这与 DuckDB 做 AI 数据接口的方向一脉相承(延伸阅读:用 DuckDB 构建 AI Agent 大脑、DuckDB MCP Server 指南)。
5. 可扩展性:全新 PEG 解析器
DuckDB 历来用从 PostgreSQL 派生的解析器。2.0 起,自家的现代 PEG 解析器(1.5 里作为 opt-in 试用)成为默认。扩展现在能钩进语法本身——官方预期会有暴露全新 SQL 语法的扩展出现。报错也带上了精确的源码位置。
为什么换?老解析器基于 YACC/Bison 生成的 LALR(1) 语法,随着 DuckSQL 扩张,任何小改动都可能撞上既有规则、引发 shift/reduce 或 reduce/reduce 冲突。PEG 按书写顺序尝试各分支,新增语法不再有不破坏无关规则的风险(Python 3.9 也是为此转向 PEG)。新解析器与旧的保持兼容,出可用 SET enable_legacy_parser = true; 切回(2.0 实测该开关存在且生效)。
自己写扩展、自己托管
C API v2 大幅增强,暴露了文件系统、表函数等组件给扩展。还能注册并托管可信扩展仓库——一个组织可签发自己的扩展并像内置一样安装加载:
SET allow_extension_repositories = 'allowed';
CREATE EXTENSION REPOSITORY my_repo WITH PREFIX 'https://extensions.example.org';
INSTALL AND LOAD my_ext FROM my_repo;
CREATE 时会拉取仓库的公钥并固化进仓库定义,打印每个 key 的 SHA-256 指纹供带外比对。该特性仍在演进,语法可能变。
6. 核心扩展更新
2.0 同时带了一批核心扩展的更新与性能优化,挑几个生产相关的:
- DuckLake 1.1:新规格 + 参考实现
- Iceberg:向"功能完整"推进,新增 REST catalog 的 ATTACH 选项(按 namespace 推导表位置,或从 catalog 返回的 metadata-location 文件读元数据)
- Delta:支持
CREATE TABLE(含分区与 Delta 表属性)、时间旅行、改进 VARIANT 支持
7. 触发器:审计表与物化视图增量维护
2.0 交付了 CREATE TRIGGER,支持 BEFORE/AFTER、FOR EACH ROW / FOR EACH STATEMENT、经 REFERENCING OLD/NEW TABLE 的过渡表、单事件多触发器、被触发表上的 RETURNING、DROP TRIGGER。经典用例是审计表——记录一次表修改时发生了什么:
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、一个 DELETE)维护"按国籍统计平均年龄"的聚合表:
CREATE TABLE people (name VARCHAR, nationality VARCHAR, age INTEGER);
CREATE TABLE age_statistics (nationality VARCHAR PRIMARY KEY,
sum_age INTEGER, num_people INTEGER, average_age DOUBLE);
CREATE TRIGGER people_insert AFTER INSERT ON people
REFERENCING NEW TABLE AS n FOR EACH STATEMENT
MERGE INTO age_statistics s USING
(SELECT nationality, sum(age) AS sum_age, count(*) AS num_people
FROM n GROUP BY nationality) d
ON s.nationality = d.nationality
WHEN MATCHED THEN UPDATE SET sum_age = s.sum_age + d.sum_age,
num_people = s.num_people + d.num_people,
average_age = (s.sum_age + d.sum_age) / (s.num_people + d.num_people)
WHEN NOT MATCHED THEN INSERT VALUES (d.nationality, d.sum_age, d.num_people, d.sum_age/d.num_people);
实测:插入 6 人后聚合表自动生成:
| nationality | sum_age | num_people | average_age |
|---|---|---|---|
| HU | 45 | 1 | 45.0 |
| NL | 87 | 3 | 29.0 |
| US | 92 | 2 | 46.0 |
官方反复强调:触发器让 DuckDB 离"物化视图"这个长期路线图又近了一步(BEFORE … FOR EACH ROW 等少数组合暂不支持,见官方限制说明)。
8. SQL 方言扩充
2.0 的方言新增,挑实用项:
$变量简写:大多数表达式位置可写$x替代getvariable('x')- 数据修改 CTE:
INSERT/UPDATE/DELETE/COPY可作为管道步骤 - NEAREST join:把 top-k 相似检索变成 join 子句,向量/嵌入场景直接受益
- 嵌套 schema:
CREATE SCHEMA finance.reports;支持 schema 里套 schema - JSON 变更函数:
json_set/json_insert/json_replace/json_remove返回修改后的 JSON 副本(详见 JSON 补丁函数实战)
实测嵌套 schema 与 JSON 变更(2.0 CLI):
CREATE SCHEMA finance;
CREATE SCHEMA finance.reports;
SHOW SCHEMAS;
-- memory | finance / main / reports 三行
SELECT json_set('{"a":1,"b":2}'::JSON, '$.c', '3'::JSON),
json_remove('{"a":1,"b":2}'::JSON, '$.b');
-- {"a":1,"b":2,"c":3} {"a":1}
另:2.0 支持 SQL 标准的 FETCH FIRST,并允许 UNNEST 出现在 GROUP BY 子句里;对多行命中时,MERGE 与 UPDATE ... FROM 的语义被定义得无歧义。
9. 迁移到 2.0:破坏性变更与注意项
升级对多数用户平滑,但多进程、生产级部署要看清这三处:
存储格式(最易踩坑)
2.0 之前的版本无法读取 2.0 创建的数据库文件;而 2.0 仍能读写旧文件、且旧文件对老版本保持可读。建议先在影子系统上验证再切生产;关键工作负载可等 11 月的首个补丁版本。
编译器要求
告别 C++11,2.0 起要求 C++17 编译器;Linux/macOS 发行构建默认改用 Clang(Windows 的 CLI 用 Clang,多数核心扩展仍是 MSVC)。
时区/日历/排序:去掉 ICU 依赖
2.0 彻底移除 ICU 库——icu 扩展现在自实现时区、日历、排序,时区数据直接由 IANA 数据库构建并压缩到约 45 kB,行为不变且更快。
弃用项
Lambda 旧单箭头语法(
x -> x + 1)默认禁用,改用 Python 风格:SELECT list_transform([1, 2, 3], lambda x : x + 1); -- [2, 3, 4] -- 旧语法 2.0 实测报: -- Binder Error: Deprecated lambda arrow (->) detected ... -- 可临时 SET lambda_syntax='ENABLE_SINGLE_ARROW' 切回,v2.1 彻底移除amalgamation 构建(把整库源码压成单 header + 单 .c)弃用——已大到多数场景不再实用
macOS x86_64 逐步退场:当前仍构建支持,但社区扩展不再为其构建,v2.1 起停止支持
三条 LTS 线
| 版本 | 支持到 | 说明 |
|---|---|---|
| 1.4 | 2026 年 11 月底 | 旧 LTS |
| 1.5 | 2027 年 3 月 | 官方延长,事实上的 LTS |
| 2.0 | 2027 年 10 月 | 新 LTS,Cinnamon Teal |
小结与下一步
2.0 的核心不是"多了 X 个函数",而是数据库形态的扩展:嵌入、服务、远程三种运行方式共存;VARIANT + 触发器 + 异步 I/O 让"分析库"真正能干"事务库"的活;PEG 解析器 + C API v2 把可定制性打开给扩展作者。
- 想在 2.0 上落地服务化:Server Mode 架构
- 想接 AI 工作流:AI Agent 大脑、MCP Server 指南
- 装与跑通:安装指南、完全指南、速查表