DuckDB 2.0.0 LTS 正式版:那只鸭子开完了服务器,转头去给 AI 打工
两个月前,2026 年 8 月 17 日,DuckDB 官方发了一篇 17 分钟长文《A Preview of DuckDB v2.0》。我当时在本站写了一篇《那只说"不搞分布式"的鸭子,食言了》,结尾给的立场很明确:
玩,别上生产。
理由也写得很具体——那是预览版,存储格式是破坏性变更,官方自己都说"细节可能还会调整"。
然后今天,2026 年 10 月 21 日,正式版发了。
代号 Cinnamon Teal(肉桂水鸭,学名 Spatula cyanoptera,一种分布在美洲西部的红褐色鸭子——也就是此前社区口口相传的 Cyanoptera)。自 DuckDB 1.5 以来累计 接近 20,000 次提交,官方的原话是"这是我们迄今最大的一次特性发布"。
所以这篇文章要干两件事:把两个月前那句"别上生产"收回一半,以及把从预览到正式之间到底变了什么讲清楚。
先把版本号这件事钉死
DuckDB 现在同时存在三条版本线,选型的同学直接抄这张表:
| 版本线 | 身份 | 社区支持到 |
|---|---|---|
| 2.0 | 最新 LTS(本次发布) | 至少 2027 年 10 月 |
| 1.5 | 被延长支持,事实上的 LTS | 2027 年 3 月 |
| 1.4 | 上一个 LTS | 2026 年 11 月底 |
还有两个日期值得记:11 月 16 日会发 2.0.1 补丁版;1.5 系列从 2.0 发布当天起就不再是"最新 stable",补丁流向直接换向,名义 EOL 是 11 月 1 日,但那之后你等不到 1.5.7。
换句话说:如果你现在是 1.4 或 1.5 跑在关键链路上,真正的支持断点就是今天,不是 11 月。而 2.0 的迁移窗口,官方给了整整一年。
顺带一提,官方还单独发了一页《What's New in the Docs for 2.0》,把下面每个特性都链到了对应文档页。这个细节我很喜欢——发版不发文档等于没发。
从预览到正式,真正值得关心的六件事
1. Quack 毕业了,还顺手认了别人家的数据库
DuckDB 从 2019 年出生那天起就是 in-process 的:没有守护进程、没有端口、不用运维。1.5.3 才第一次掏出 Quack 协议,2.0 里这玩意儿从预览打磨毕业,性能和稳定性都上了一个台阶,并且带来一个新语句 CONNECT——挂载和切换远程库,一条命令搞定:
-- 服务端
CALL quack_serve(token = 'my_token');
-- 客户端
ATTACH 'quack:localhost' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT * FROM events;CLI 甚至给了快捷开关,开两个终端就能玩:
duckdb -serve # 终端 A,直接起服务
duckdb -connect # 终端 B,直接连上但真正的杀招在这里:CONNECT 不只认 DuckDB。它对 PostgreSQL、MySQL 一样有效,而且新的远程下推优化器会把 SQL 直接扔到远端服务器执行,而不是把整张表拖过网络再算。
这意味着 DuckDB 悄悄从"一个库文件"变成了"一个可以当统一 SQL 入口的客户端"。你连着 Postgres、挂着 S3 上的 Parquet、ATTACH 着 DuckLake,然后用同一套 SQL 方言跨着查——这个想象空间比"DuckDB 有服务器了"大得多。
安全上有个坑要提醒:quack_serve 默认只监听 localhost。要对外,官方明确建议保持 localhost 并放在 TLS 终止的反向代理后面,那个 allow_other_hostname 选项是为测试准备的,别在生产上开。
2. 异步 IO:S3 上的查询终于不憋屈了
2.0 给 Parquet 和 CSV 的读取引入了全引擎异步 IO。I/O 层现在可以独立于查询处理层伸缩,后台发读请求、多个请求同时在飞。
一句话总结体感:**本地盘是小幅提升,对象存储上是数量级的变化。**如果你的日常是 SELECT * FROM 's3://bucket/logs/*.parquet',这条是你升级之后最先感觉到的。
3. VARIANT:从"能用"变成"一等公民"
VARIANT 在 1.5 就登场了,但 2.0 才算真正长开。官方给的定义我很喜欢——"JSON on steroids",打了激素的 JSON。
和普通 JSON 一样每一行能长成不同形状,不一样的是它不是文本。2.0 会自动嗅出半结构化数据里藏着的公共结构,把它 shred(切碎)成列式表示:压缩友好、执行飞快,而且你不用声明 schema。整条链路端到端打通,Parquet 的 shredded 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), variant_keys(payload) FROM events;┌───────────────────────┬───────────────────────┐
│ variant_type(payload) │ variant_keys(payload) │
├───────────────────────┼───────────────────────┤
│ OBJECT │ [user] │
│ OBJECT │ [event, x, y] │
└───────────────────────┴───────────────────────┘日志摄取、Agent 工具调用轨迹、请求响应 payload——这类"结构相似但天天在变"的数据,以前你要么硬套 schema 天天改表,要么塞进 TEXT 字段然后祈祷别太慢。现在不用选了。
4. Agent Mode:这条最有时代感
这是我在 2.0 里最喜欢的一条,也是两个月前的预览文里没有重点讲的。
现在 Claude Code、Codex、Cursor 这类编码智能体,越来越多地在替用户驱动 DuckDB CLI。于是 2.0 的 CLI 会检测自己是不是被 Agent 跑着,是的话自动切进 agent mode:
- 表格输出从带 padding 的方框改成紧凑 Markdown;
- 结果被截断时会明确说出来,而不是默默少给几行;
- 失控的查询提前掐断;
- 报错以 JSON 形式返回;
- 长查询先报预估代价,让 Agent 自己决定要不要等。
官方在 TPC-H 上测了一组:agent mode 让模型需要读的 DuckDB 输出量减少了 59%。
59% 是什么概念?按 token 计费就是接近六成的账单蒸发。一个数据库为了让自己"更适合被 AI 读",专门改了输出格式——这件事在三年前听起来很荒诞,在 2026 年是最正常的产品决策。
想手动体验可以加 -agent,反过来想让 Agent 别用就 -no-agent。
5. 解析器换血,扩展可以自己开仓库了
DuckDB 从 2018 年第一个 commit 起用的都是从 PostgreSQL 继承来的 YACC/Bison 解析器(LALR(1))。这种文法有个老毛病:语法一长大,加一条新规则就可能和老规则撞车,报 shift/reduce 冲突。
2.0 里官方自己写的 PEG 解析器正式转正为默认。PEG 按书写顺序试各条备选,加语法不再有撞车风险;扩展甚至能直接挂到文法上,注册全新的 SQL 语法。报错信息也带上了精确源码位置。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;仓库创建时 DuckDB 会拉取公钥并固定(pin)进定义里,打印 SHA-256 指纹供你带外核对。前缀可以指向本地路径、https、s3——只要 DuckDB 能读。对企业来说,扩展管理终于能纳入自己的基础设施管控范围,不用全押官方仓库。
6. 触发器,以及一堆"终于有了"的 SQL
触发器在 2.0 里补齐了: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;但官方给的另一个例子更让我兴奋——用触发器增量维护物化视图。插入时按 nationality 聚合 merge 进统计表,删除时减回去并清掉归零的分组。官方原话是:"触发器让我们离物化视图又近了一步",而物化视图在他们 roadmap 的 future work 里躺了很多年。
SQL 方言这边还有一堆顺手加的:
NEARESTjoin:把 top-k 相似度搜索变成一条 join 子句,向量/embedding 场景直接受益;- 数据修改 CTE:
INSERT/UPDATE/DELETE/COPY可以当流水线的一步写进 CTE; $x简写:多数表达式位置不用再写getvariable('x');- 嵌套 schema:
CREATE SCHEMA finance.reports; - JSON 变更函数:
json_set、json_insert、json_replace、json_remove; - 还有标准 SQL 的
FETCH FIRST、GROUP BY里允许UNNEST,以及MERGE/UPDATE ... FROM在多行匹配时的语义终于有明确定义。
顺带,同日还发了 DuckLake 1.1(含新规范和参考实现),Iceberg 扩展补了 REST catalog 的 ATTACH 选项,Delta 扩展支持 CREATE TABLE(含分区和表属性)、时间旅行和 VARIANT。
谁在用,省了多少钱
Spotify 在 DuckCon 上分享过把 DuckDB 当用户收听历史之上的 SQL 层,官方 40k Star 博客里专门点了名。
Arcesium(华尔街金融科技)在 2026 年 6 月 30 日的工程博客里写了他们从 Athena 迁到 DuckDB 的 18 个月:数千条生产 SQL、数十个金融客户、复杂的投资组合计算,整体成本砍半。他们也没藏着掖着——Schema 推断会踩坑,S3 连接不显式指定 ENDPOINT 就报 IO Error,有些参数文档里不好找得读源码。作者的结论很实在:这条路不容易,但对金融科技这种查询多变、并发高、不依赖 PB 级扫描的场景,值得这笔迁移成本。
Definite(分析平台创业公司)把整个栈建在 DuckDB + DuckLake 上,账单是这么个画风:
| 组件 | Snowflake + Looker | Definite (DuckDB/DuckLake) |
|---|---|---|
| 计算 | $2,000–5,000/月 | 包含 |
| 存储 | $40–80/月 | $20–40/月 |
| BI 工具 | $1,000–3,000/月 | 包含 |
| 连接器 (Fivetran) | $500–2,000/月 | 包含 |
| 总计 | $3,500–10,000/月 | $250–500/月 |
这不是省 20%,这是差一个数量级。他们的仪表盘典型查询在 200–400ms,而 Snowflake X-Small 需要 2–5 秒。
MotherDuck(DuckDB 的商业云服务)2026 年 Q1 付费团队数突破了 10,000。
项目管理上也有个背景值得知道:DuckDB 背后的公司 DuckLabs 明确拒绝过风险投资,理由是不想让项目方向被推向变现;IP 由独立非营利组织 DuckDB Foundation 持有,章程保证 MIT 协议永久开源。2026 年 8 月底 DuckLabs 宣布加入 AWS,团队仍留在阿姆斯特丹,官方同期更新的支持政策里承诺加大文档、教育和社区投入,并在今年秋天增设 Technical Advisory Board。
初体验:五分钟跑起来
CLI 和 Python 客户端今天已经放出来了(Go、R、Java 和部分扩展会晚几天)。
pip install -U duckdb
python -c "import duckdb; print(duckdb.__version__)"CLI 用户去 GitHub Releases 抓对应平台构建,解压即用。
玩一下服务器模式,开两个终端:
duckdb -serve # 终端 A
duckdb -connect # 终端 B玩一下 VARIANT:
CREATE TABLE logs (payload VARIANT);
INSERT INTO logs VALUES
('{"level":"ERROR","msg":"disk full","host":"a-01"}'::JSON::VARIANT),
('{"level":"INFO","msg":"ok","host":"a-02"}'::JSON::VARIANT);
SELECT variant_extract(payload, '$.level') AS lvl, count(*)
FROM logs GROUP BY lvl;玩一下触发器(审计表最小闭环):
CREATE TABLE t (id INTEGER, val VARCHAR);
CREATE TABLE audit (id INTEGER, old_val VARCHAR, new_val VARCHAR);
CREATE TRIGGER trg_audit AFTER UPDATE ON t
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;上生产之前,先把这四条读完
两个月前我说"别上生产",现在我把话改成——可以开始迁,但按这四条来。
存储格式是破坏性变更。2.0 之前的 DuckDB 读不了 2.0 创建的库文件;反过来 2.0 能读写你的老库文件,且写出来的老文件仍能被老版本读。所以升级前先想清楚共享文件的版本锁定策略。
lambda 语法默认换了。旧的单箭头
x -> x + 1已默认禁用,改成 Python 风格:sqlSELECT list_transform([1, 2, 3], lambda x: x + 1);临时回退用
SET lambda_syntax = 'enable_single_arrow';,但这个开关和旧语法会在 v2.1 彻底移除,别把它当长期方案。ICU 依赖被移除了。时区、日历、排序规则由
icu扩展自己实现,时区数据直接由 IANA 库压到约 45 kB——行为不变,而且更快(2500 万行时区转换 0.24s → 0.11s)。平台与构建:编译器要求升到 C++17,发布构建默认换 Clang;amalgamation 构建已弃用;macOS x86_64 进入淘汰流程,2.1 起停止支持,社区扩展已经不再为该平台构建。
最后一条来自官方自己的建议,我原样转述:先在影子系统上测 2.0.0,再切生产;如果是关键业务负载,可以等 11 月的第一个补丁版。
一只 2019 年靠"我就是不开服务器"出圈的鸭子,2026 年有了自己的网络协议、学会了跨库下推、把输出格式改成 AI 更爱读的样子,还给自己挂了个 LTS 标签。它不是背叛了初衷——它只是从"你不需要集群"走到了"你依然不需要集群,但你想连的时候能连"。单二进制的简洁性它还没丢,这已经相当克制了。
进阶
想系统学 DuckDB,可以先看认识DuckDB和玩转DuckDB。想看它在超大规模 CSV 上的表现,玩转超亿级CSV和百亿级数据性能测试里有完整的实测数据。两个月前那篇2.0 预览版解析可以作为对照阅读。
官方那份 2.0 发布长文、异步 IO 与 PEG 解析器的专题博客,以及《What's New in the Docs for 2.0》导航页,都值得通读一遍。
更多开源技术干货和学习资料,关注公众号「遇码」,领取专属福利。
