Hotdry.

Article

DuckDB Quack 协议解析:HTTP 驱动的客户端-服务端通信设计

深入解析 DuckDB Quack 协议的设计哲学:基于 HTTP 的请求-响应模式、application/duckdb 序列化格式,以及相比 Arrow Flight SQL 和 PostgreSQL 协议的性能优势与适用场景。

2026-05-13systems

在数据库系统的演进历程中,客户端 - 服务端架构与嵌入式单机架构代表了两种截然不同的设计哲学。前者以 PostgreSQL 为代表,强调集中式状态管理与并发访问控制;后者以 SQLite 和 DuckDB 为代表,追求进程内高速数据访问与零协议开销。2026 年 5 月,DuckDB 团队正式发布 Quack 协议(Quack: The DuckDB Client-Server Protocol),标志着这款以嵌入式分析见长的数据库正式拥抱客户端 - 服务端架构。这一决策并非源于技术崇拜,而是源于真实的产品需求:当多个进程需要同时修改同一个数据库文件时,传统的文件锁机制已无法满足多写入者并行工作的场景需求。

设计哲学:从进程内到多进程的关键跨越

DuckDB 诞生之初便确立了 in-process 架构的核心定位,这一选择在数据科学交互式分析场景中展现出显著优势 —— 无需网络开销,数据直接在 Python 或 R 进程中完成查询与分析。然而,这种设计在面对多进程写入场景时遭遇了根本性挑战:DuckDB 在主内存中维护大量状态信息,多个进程同时修改这些共享状态需要复杂的同步机制,而这种同步在工程复杂度与性能上均难以接受。

在 Quack 协议出现之前,社区已探索了多种变通方案。部分开发者采用自定义 RPC 服务封装 DuckDB 实例,为其他进程提供查询与写入接口;MotherDuck 团队开发了专属的客户端 - 服务端协议;GizmoSQL 等项目则基于 Arrow Flight SQL 协议实现 DuckDB 的远程访问能力。这些尝试虽然解决了部分问题,但均存在各自的局限性 —— 自定义 RPC 缺乏标准化支持,Arrow Flight SQL 在事务型小写入场景表现欠佳,且这些方案都未能实现与 DuckDB 核心架构的深度整合。

Quack 协议的诞生代表了 DuckDB 团队对这一问题的正式回应。与其依赖外部协议或补丁式扩展,不如从零开始设计一套专为 DuckDB 打造的客户端 - 服务端通信机制。这使得团队能够充分利用 DuckDB 内部的高效序列化原语,同时避免受制于外部协议规范的演进节奏。协议名称本身也体现了 DuckDB 社区的幽默感 —— 当两只或更多鸭子需要相互交流时,它们会 quack(嘎嘎叫),因此这个让两个 DuckDB 实例相互通信的协议自然被命名为 Quack。

核心架构:HTTP 传输层与请求 - 响应模式

Quack 协议在传输层选择 HTTP 而非自定义 TCP 协议,这一决策基于对 2026 年网络基础设施现状的深入考量。HTTP 协议自 CERN 诞生以来已演进超过三十年,已成为网络通信的事实标准,几乎所有现代基础设施 —— 负载均衡器、防火墙、入侵检测系统、代理服务器 —— 都对 HTTP 提供了原生支持。选择 HTTP 意味着 Quack 可以无缝融入现有的运维体系,而无需为数据库流量单独配置网络策略。

从协议交互模式来看,Quack 采用经典的请求 - 响应架构,所有交互均由客户端驱动。连接建立阶段,客户端首先发送连接请求并附带认证令牌(Token),服务端验证通过后建立会话;随后的交互包括执行查询并获取首部分结果集,以及通过 fetch 消息获取大型结果集的后续分片。值得注意的是,Quack 在设计上追求单次往返完成查询执行 —— 对于简单查询而言,客户端发送查询请求后,服务端可直接返回完整结果,整个交互仅需一个请求 - 响应对。

协议栈的默认监听端口为 9494,这个数字的选择颇具深意:1994 年是 Netscape Navigator 发布的年份,标志着互联网商业化的开端。选择这一易于记忆的数字作为默认端口,降低了用户的配置负担。端口号可通过配置参数灵活修改,适应不同的部署环境需求。

在序列化格式方面,Quack 采用了自定义的 application/duckdb MIME 类型。这一选择背后存在重要的技术考量:虽然 Arrow Flight SQL 等现有协议提供了标准化的数据交换格式,但 DuckDB 内部的数据结构与 Arrow 格式之间存在差异 —— 尽管两者在某些方面接近,但在类型系统和结果集表示上仍有分歧。Quack 协议直接复用 DuckDB 内部的高效序列化原语,这些原语已在 WAL(Write-Ahead Log)文件中经过多年生产环境验证,既保证了序列化效率,也确保了与 DuckDB 核心引擎的零成本集成。此外,采用自研序列化格式还意味着团队可以在不依赖外部规范的情况下自由添加新的数据类型或协议消息,保持协议演进的灵活性。

安全模型:认证可扩展与传输层建议

作为一款可能暴露于网络的数据库协议,Quack 在安全性设计上采取了务实的分层策略。默认配置下,Quack 服务器会在启动时自动生成随机认证令牌,客户端连接时必须提供与此令牌匹配的身份凭证。这种设计确保了开箱即用的安全性 —— 即使开发者忘记手动配置认证,服务器也不会处于完全开放状态。默认情况下,Quack 服务器仅绑定到 localhost 接口,这意味着即使未配置认证,外部网络也无法直接访问数据库实例。

然而,Quack 团队明确建议不要将 Quack 端点直接暴露于公共互联网。对于需要公网访问的场景,应当在 Quack 服务器前端部署反向代理(如 nginx),由代理终止 SSL/TLS 连接。Quack 本身默认不启用 SSL 的原因是针对本地通信场景,引入 SSL 基础设施会带来不必要的复杂度和性能开销。但协议设计充分考虑了非本地连接的 SSL 假设 —— 当客户端检测到连接目标为非 localhost 地址时,将默认期望 SSL 加密传输,这一行为可通过配置参数覆盖。

在认证与授权的可扩展性方面,Quack 设计了一套精巧的回调机制。服务端提供认证回调(Authentication Callback)和授权回调(Authorization Callback)两个扩展点。默认的认证回调实现仅比较客户端提供的令牌与启动时生成的随机令牌是否匹配,但开发者可通过配置将自己的认证函数接入 —— 这些函数可以是查询 LDAP 目录、读取配置文件,甚至是一个随机掷骰子的逻辑。授权回调同样支持自定义,开发者可以在回调中检查每个待执行的查询,实现细粒度的权限控制。这两个回调函数甚至可以是普通的 SQL 宏(SQL Macro),进一步降低了扩展门槛。

性能基准:批量传输与小事务的平衡

Quack 协议的性能表现是衡量这一设计成功与否的关键指标。DuckDB 团队在 AWS m8g.2xlarge 实例(8 vCPU,32GB 内存)上进行了两组基准测试,模拟客户端与服务器位于同一数据中心不同机器的场景,节点间平均延迟约 0.28 毫秒。

第一组测试聚焦批量传输场景 —— 这是传统数据库协议的痛点所在。测试使用 TPC-H lineitem 表的数据,传输规模从 10 万行逐步扩展至 6000 万行(原始 CSV 约 76GB),并与 PostgreSQL 协议和 Arrow Flight SQL 协议进行对比。结果显示,Quack 在所有测试规模下均表现最优:10 万行传输耗时 0.07 秒,与 Arrow Flight 持平但远超 PostgreSQL 的 0.20 秒;6000 万行传输仅需 4.94 秒,Arrow Flight 需 17.40 秒,而 PostgreSQL 协议则需惊人的 158.37 秒。这一巨大差距源于 Quack 针对批量数据传输的专门优化,充分利用了多线程并行拉取结果集的能力。

第二组测试评估小写入场景 —— 每次插入单行,每个插入作为独立事务提交。这模拟了遥测数据收集等常见用例:多个进程向中央 DuckDB 实例写入观测数据,同时可能有其他进程在查询这些数据。测试采用逐步增加并发线程数的方式,持续 5 秒后统计每秒事务数。结果同样令人振奋:单线程下 Quack 可达 1038 事务 / 秒,PostgreSQL 为 839 事务 / 秒,Arrow Flight 仅 469 事务 / 秒;8 线程并发时 Quack 达到 5434 事务 / 秒,PostgreSQL 为 4320 事务 / 秒,Arrow Flight 为 1358 事务 / 秒。Quack 在小事务场景下超越 PostgreSQL 的关键在于单次往返完成查询的能力 ——Arrow Flight SQL 的设计要求每个查询至少两次协议往返(CommandStatementQuery + DoGet),在高延迟环境下的劣势尤为明显。

配置实践:服务端启动与客户端连接

Quack 协议以扩展形式发布,初始集成在 core_nightly 仓库中,可在 DuckDB v1.5.2 及以上版本安装使用。服务端配置首先需要安装并加载 quack 扩展,然后调用 quack_serve 函数指定监听地址和认证参数。以下是一个典型的服务端配置示例:

INSTALL quack FROM core_nightly;
LOAD quack;

CALL quack_serve(
    'quack:localhost',
    token = 'your_secure_token'
);

CREATE TABLE telemetry AS 
    SELECT * FROM read_parquet('s3://data/telemetry/*.parquet');

客户端配置需要创建相应的 quack 类型密钥(Secret),指定连接协议、地址和令牌,然后通过 ATTACH 语句将远程数据库挂载为本地模式的一个 schema:

INSTALL quack FROM core_nightly;
LOAD quack;

CREATE SECRET (
    TYPE quack,
    TOKEN 'your_secure_token'
);

ATTACH 'quack:localhost' AS remote;

-- 直接查询远程表
SELECT count(*) FROM remote.telemetry;

-- 通过 remote.query 函数推送完整查询到远程执行
FROM remote.query('SELECT date_trunc('hour', ts) AS hour, 
                          count(*) AS cnt 
                   FROM telemetry 
                   GROUP BY 1');

上述配置示例展示了 Quack 的两种使用模式:直接引用远程表时,查询引擎会自动将操作下推至远程服务器执行;对于复杂查询或需要精确控制执行位置的场景,可使用 remote.query() 函数将完整 SQL 语句作为字符串发送给远程服务器处理。对于涉及大规模数据移动的操作(如将本地结果集插入远程表),同样可以直接使用 INSERT INTO remote.table SELECT ... 语法,DuckDB 优化器会处理分布式执行计划的生成与结果路由。

应用场景与生态整合

Quack 协议的发布为 DuckDB 开辟了全新的应用领域。在集中式数据采集场景中,多个数据源进程可以并行向中央 DuckDB 实例写入数据,同时仪表盘查询可以实时读取最新数据 —— 这解决了嵌入式模式下多进程写入必须串行化的问题。微服务架构中,各服务可使用轻量的 DuckDB 实例管理本地数据,并通过 Quack 协议聚合到中央分析节点,既保留了边缘计算的低延迟优势,又实现了全局数据可视化管理。

DuckDB 团队已规划将 Quack 与 DuckLake 深度整合,使远程 DuckDB 服务器可直接作为 DuckLake 的目录服务使用。这一整合将大幅提升数据内联(data inlining)等操作的性能,并为分布式数据架构提供更灵活的访问模式。此外,WASM 版本的 DuckDB(duckdb-wasm)将原生支持 Quack 协议,这意味着运行在浏览器中的 DuckDB 实例可以直接连接到云端服务器,实现纯前端的分布式数据分析能力。

未来演进路线

Quack 协议的计划发布时间与 DuckDB v2.0 保持一致,预计于 2026 年秋季随主版本发布正式生产版本。在那之前,团队将重点完善自动安装与加载机制 —— 未来当 SQL 语句引用远程 Quack 资源时,DuckDB 将自动安装并加载 quack 扩展,降低使用门槛。在并发事务处理能力方面,当前基准测试显示 Quack 在超过 8 个并行写入线程后遭遇 DuckDB 自身的扩展瓶颈,团队计划在 v2.0 中大幅提升单表并发插入性能。

更长远的方向包括协议扩展能力 —— 允许 DuckDB 扩展为 Quack 协议添加新的消息类型和处理逻辑,以及基于 Quack 的复制协议,用于构建主从复制的只读集群。这些演进将进一步巩固 DuckDB 从嵌入式分析引擎向现代数据架构核心组件转型的战略方向。

资料来源:DuckDB 官方博客《Quack: The DuckDB Client-Server Protocol》(https://duckdb.org/2026/05/12/quack-remote-protocol)

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com