DuckDB 悄悄变客户端-服务端数据库,UPDATE 有坑
2026 年 5 月 12 日,DuckDB 悄悄发布了一个叫 Quack 的东西:它把一直"只在你进程里"的 DuckDB,变成了一个可以网络访问、多客户端并发读写的客户端-服务端数据库。目前 beta(v1.5.3 起可用,计划 2026 年 9 月随 v2.0 转 stable)。
今天这篇,是真机实测。我会把官方宣传里没说透的地方,用真实报错给你打出来。
● ● ●
一、Quack 是什么,一句话
任何两个 DuckDB 进程,一个调quack_serve()变服务器,另一个用quack:协议连过去——就成了客户端-服务端架构。协议跑在 HTTP 上(防火墙、负载均衡、反向代理全兼容),请求响应用的是 DuckDB 内部序列化(和 WAL 同一条代码路径),复杂类型跨网络无损失。
对数据团队最实际的三个场景:
- 01
数据在服务器上,你想把计算挪到数据旁边,本地只看结果; - 02
多个进程要并发读写同一个库(原生 DuckDB 文件不支持多进程并发写); - 03
服务器很强,本地很弱,把重查询甩过去。
官方 benchmark:8 核 32G 的机器上,Quack 能扛几千 writes/s,够中小 OLTP 用了。
● ● ●
二、30 秒跑起来
服务器端(任意一个已有 DuckDB 会话):
CALL quack_serve('quack:localhost:9494', token = 'your_token_here');客户端:
CREATE SECRET (TYPE quack, TOKEN 'your_token_here');
ATTACH 'quack:localhost:9494' AS remote;
SELECT * FROM remote.some_table;
两个新手坑,我替你踩过了:
- token 至少 4 个字符
,短了直接报 Quack server token must be at least 4 characters long; 本机 localhost自动走 HTTP,连远端默认自动切 HTTPS(可用DISABLE_SSL覆盖)。
到这里,看起来一切都和宣传一致:"Quack protocol supports the full DuckDB feature set – over the wire."
完整功能,对吧?往下读。
● ● ●
三、反转:ATTACH 完了,我 UPDATE 了一下
我按最直觉的用法,把远端库挂上来,开始标准 CRUD:
CREATE TABLE remote.orders (id INTEGER, product VARCHAR, total DOUBLE); -- ✅
INSERT INTO remote.orders VALUES (1,'widget',42.0),(2,'gadget',7.5); -- ✅
SELECT * FROM remote.orders; -- ✅
UPDATE remote.orders SET total=49.99 WHERE id=1; -- 💥
真实报错(DuckDB 1.5.5,原样):
Binder Error: Can only update base tableDELETE 也一样:
Binder Error: Can only delete from base table也就是说:用 ATTACH 挂上来的远端表,能建、能插、能查、能删表,唯独不能改行、不能删行。 在 DuckDB 的 binder 眼里,远端表不是 "base table"。
所以宣传里那句 "full feature set over the wire",在 ATTACH 这条路径上,是不成立的。我猜这是 beta 阶段的限制,但官方文档和 FAQ 里,我通读之后没有找到任何一处提到这一点。
(顺带一提,挂上去的远端 catalog 也比较"裸":remote.information_schema.tables 和 remote.duckdb_tables() 都不存在,元数据内省能力目前有限。)
● ● ●
四、正确姿势:quack_query
好消息是 Quack 提供了第二条路:不挂载,直接用无状态的 quack_query(uri, sql) 表函数,SQL 在服务器端执行:
FROM quack_query('quack:localhost:9494',
'UPDATE orders SET total = 49.99 WHERE id = 1',
token = 'your_token_here');
实测,完整 CRUD 全通:
| UPDATE | ||
| DELETE | ||
Quack 双路径架构图
所以结论是:读和分析走 ATTACH(可以 JOIN、聚合、当数据源用),行级改删走 quack_query。 两条路配合着用,才是当前 beta 的完整玩法。
● ● ●
五、事务:能,但姿势有讲究
quack_query 是无状态的——每次调用都是独立请求,不共享会话状态。所以别这么写:
-- ❌ 这样不行:ROLLBACK 会报
-- "cannot rollback - no transaction is active"
quack_query(..., 'BEGIN TRANSACTION');
quack_query(..., 'INSERT INTO t VALUES (1)');
quack_query(..., 'ROLLBACK');
正确做法:把事务打包进同一个字符串,在服务器端原子执行:
FROM quack_query('quack:localhost:9494',
'BEGIN TRANSACTION;
INSERT INTO t VALUES (1, 10), (2, 20);
COMMIT;',
token = '...');
实测 COMMIT / ROLLBACK 都正常,原子性成立。
● ● ●
六、边界清单(beta 阶段的实话)
- beta,会 break。
官方原话:协议、函数名、默认值都还可能变。生产别押,原型随便玩;稳定版等 2026-09 的 v2.0。 - 不支持分布式查询。
计算要么在客户端要么在服务器,不跨节点拆分。 - 无状态调用不共享会话
,事务要打包。 - 远端 catalog 能力有限
,内省靠 SELECT * FROM quack_query(...)之类的绕行。 token ≥ 4 字符;默认端口 9494;对外暴露务必上反向代理 + TLS(官方有专门的安全文档)。
● ● ●
七、谁该现在就用,谁该再等等
现在就用:
被" DuckDB 文件不能多进程并发写"卡住的(多个服务要写同一份数据); 想把重查询从弱客户端甩到强服务器的; 想给内部工具加个"轻量数据库 API"又不想引入 Postgres 运维成本的。
再等等:
生产关键路径(等 v2.0 stable); 需要跨节点分布式计算的; 依赖完整 catalog 内省 / 工具链生态的(目前 ATTACH 路径对元数据工具不太友好)。
● ● ●
写在最后
Quack 的方向我很看好——DuckDB 一直赢在"零运维 + SQL 能力",Quack 补上的正是"多客户端"这块最硬的短板,而且选了 HTTP 这种最无聊也最稳的传输。
但它现在最需要的不是更多宣传,而是把行为边界写清楚。"full feature set" 这句话,在 beta 阶段,是会坑人的。
以上全部基于 DuckDB 1.5.5 实机复现,服务器端 quack_serve + 独立客户端进程。踩坑细节和完整脚本,欢迎来评论区对拍。
参考来源:
- 01
DuckDB Quack 官方概览 - https://duckdb.org/docs/extensions/quack/overview - 02
DuckDB Quack 参考手册 - https://duckdb.org/docs/extensions/quack/reference - 03
DuckDB Quack 安全指南 - https://duckdb.org/docs/extensions/quack/security - 04
DuckDB Quack FAQ - https://duckdb.org/docs/extensions/quack/faq - 05
2026-05-12 发布博客 - https://duckdb.org/2026/05/12/quack