Skip to content

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被延长支持,事实上的 LTS2027 年 3 月
1.4上一个 LTS2026 年 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——挂载和切换远程库,一条命令搞定:

sql
-- 服务端
CALL quack_serve(token = 'my_token');

-- 客户端
ATTACH 'quack:localhost' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT * FROM events;

CLI 甚至给了快捷开关,开两个终端就能玩:

bash
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_* 函数族。

sql
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 解析器,是同一个理由。

万一你真的被新解析器咬到了,临时回退:

sql
SET enable_legacy_parser = true;

扩展这边,2.0 带来了大幅增强的 C API v2(把文件系统、表函数这些组件都暴露出去),以及一个对企业意义很大的能力——自建扩展仓库:

sql
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。经典用法是审计表:

sql
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 方言这边还有一堆顺手加的:

  • NEAREST join:把 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 + LookerDefinite (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 和部分扩展会晚几天)。

bash
pip install -U duckdb
python -c "import duckdb; print(duckdb.__version__)"

CLI 用户去 GitHub Releases 抓对应平台构建,解压即用。

玩一下服务器模式,开两个终端:

bash
duckdb -serve      # 终端 A
duckdb -connect    # 终端 B

玩一下 VARIANT:

sql
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;

玩一下触发器(审计表最小闭环):

sql
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;

上生产之前,先把这四条读完 ​

两个月前我说"别上生产",现在我把话改成——可以开始迁,但按这四条来。

  1. 存储格式是破坏性变更。2.0 之前的 DuckDB 读不了 2.0 创建的库文件;反过来 2.0 能读写你的老库文件,且写出来的老文件仍能被老版本读。所以升级前先想清楚共享文件的版本锁定策略。

  2. lambda 语法默认换了。旧的单箭头 x -> x + 1 已默认禁用,改成 Python 风格:

    sql
    SELECT list_transform([1, 2, 3], lambda x: x + 1);

    临时回退用 SET lambda_syntax = 'enable_single_arrow';,但这个开关和旧语法会在 v2.1 彻底移除,别把它当长期方案。

  3. ICU 依赖被移除了。时区、日历、排序规则由 icu 扩展自己实现,时区数据直接由 IANA 库压到约 45 kB——行为不变,而且更快(2500 万行时区转换 0.24s → 0.11s)。

  4. 平台与构建:编译器要求升到 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》导航页,都值得通读一遍。

更多开源技术干货和学习资料,关注公众号「遇码」,领取专属福利。

遇码MeetCoding 开源技术社区