Quack:DuckDB 的客户端-服务器协议

Hacker News Top 工具

摘要

DuckDB 引入了名为“Quack”的全新客户端-服务器协议,使得 DuckDB 实例能够通过 HTTP 进行通信,在支持并发写入和远程访问的同时,保持简单性与高性能。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/05/13 00:24

# Quack:DuckDB 客户端-服务器协议 来源:https://duckdb.org/2026/05/12/quack-remote-protocol *摘要:DuckDB 实例现在可以通过 Quack 远程协议进行通信。这允许你在客户端-服务器架构中运行 DuckDB,并支持多个并发写入者。秉承 DuckDB 的一贯精神,Quack 设置简单,并基于 HTTP 等经过验证的技术构建。它还非常快速,能够支持从批量操作到小型事务的各种工作负载。* ## 背景:数据库架构 (https://duckdb.org/2026/05/12/quack-remote-protocol#background-database-architectures) 当数据库最初出现时,“客户端”和“服务器”之间没有区别,整个数据库只是在单台计算机上运行。在 80 年代某个时候,Sybase (https://en.wikipedia.org/wiki/Sybase) 率先引入了数据库“服务器”和在计算机上运行的“客户端”的概念。从那以后,人们理所当然地认为每个数据库系统都使用客户端-服务器架构以及在这些实体之间进行通信的协议。这很方便,因为单一的可变状态保留在服务器控制下的一个位置,同时可以有许多客户端读取和写入数据。当然,这种方法也有缺点,最显著的是,这些协议可能会增加大量的开销。如果你对此感兴趣,我们之前写了一篇关于数据库协议的论文 (https://duckdb.org/library/dont-hold-my-data-hostage/)。 当然,对于客户端/服务器架构一直有不同意见者,最著名的是 2000 年的无处不在的 SQLite (https://sqlite.org/),以及当然,DuckDB,最早于 2019 年发布。我们确实制造了很多 (https://www.youtube.com/watch?v=9OFzOvV-to4) 噪音 (https://www.youtube.com/watch?v=5ddoZR6PYNU) 关于实现进程内架构,其中没有客户端/服务器,没有协议,只有低级 API 调用。这对于交互式用例非常有效,例如,数据科学家在 Python 笔记本中与数据交互,而他们的数据由在同一进程中运行的 DuckDB 实例管理。对于 DuckDB 只是“粘合”到现有应用程序以提供 SQL 功能的许多用例来说,这也非常有效。 进程内系统在尝试从多个进程同时修改同一个数据库文件时工作得“不太理想”。有很多相关用例,例如,当从一堆收集遥测数据的进程插入到同一个数据库时,同时查询相同的表来驱动仪表板。我们不能让它工作的技术原因有很多,最主要的是,DuckDB 在主内存中保持大量状态,如果多个进程同时开始更改,必须同步该状态。 是的,有变通办法。当然,你可以快速搭建一个自定义远程过程调用 (https://en.wikipedia.org/wiki/Remote_procedure_call) (RPC) 解决方案,其中有一个进程持有 DuckDB 数据库实例,并向其他进程提供查询和插入数据的服务。还有一些项目将客户端/服务器功能 retrofit 到 DuckDB 上,例如使用 Arrow Flight SQL 协议 (https://arrow.apache.org/docs/format/FlightSql.html)。MotherDuck (https://motherduck.com/) 拥有自己的自定义客户端-服务器协议。当然,你总是可以(倒吸一口凉气)切换到更传统的数据库系统,例如同样无处不在的 PostgreSQL。然后你甚至可以运行所谓的“EleDucken (https://en.wikipedia.org/wiki/Turducken)”,即在所述 PostgreSQL 中使用 DuckDB,利用各种启用此功能的扩展之一,例如 pg_duckdb (https://github.com/duckdb/pg_duckdb)。 人们构建的大量变通办法将客户端-服务器解决方案附加到 DuckDB 上,至少说服了我们,这是人们关心的问题。我们将 DuckDB 视为通用的数据处理工具。如果这意味着除了进程内功能外还有客户端-服务器协议——很好。如果这最终解锁了 DuckDB 可以有用的大量新案例——太棒了!最终,我们非常关心用户体验,而不是在架构上拥有最后发言权。所以我们咬紧牙关,最终,终于,今天我们很高兴地宣布结果: ## 推出 DuckDB 的 Quack 协议 (https://duckdb.org/2026/05/12/quack-remote-protocol#introducing-the-quack-protocol-for-duckdb) 如果两只(或更多)鸭子想要互相交流,它们会做什么?它们会 quack (https://en.wikipedia.org/wiki/Duck#Communication)!所以,我们需要将两个 DuckDB 实例可以用来互相交流的协议称为“Quack”也是很自然的!我们有机会在 2026 年从头设计一个数据库协议,而不必考虑任何遗留问题,这是一种奢侈。我们可以从现有协议中学习,包括最近的 Arrow Flight SQL 和其他协议。在我们深入研究 Quack 如何内部工作之前,让我们从用户角度看看它是如何工作的。首先,你需要两个 DuckDB 实例。没错,DuckDB 将同时充当客户端和服务器!这两个实例可以在相隔甚远的不同计算机上(或在太空中),或者只是笔记本电脑上的两个不同终端窗口。首先,我们需要在两个 DuckDB 实例中安装 Quack 扩展。目前,Quack 位于 `core_nightly` 仓库中,并在 DuckDB v1.5.2 (https://duckdb.org/install/) 当前发布版本中可用: #### DuckDB #1 (https://duckdb.org/2026/05/12/quack-remote-protocol#duckdb-1) `` INSTALL quack FROM core_nightly; LOAD quack; CALL quack_serve( 'quack:localhost', token = 'super_secret' ); CREATE TABLE hello AS FROM VALUES ('world') v(s); `` quack: #### DuckDB #2 (https://duckdb.org/2026/05/12/quack-remote-protocol#duckdb-2) `` INSTALL quack FROM core_nightly; LOAD quack; CREATE SECRET ( TYPE quack, TOKEN 'super_secret' ); ATTACH 'quack:localhost' AS remote; FROM remote.hello; `` 这应该在 DuckDB #2 中显示远程表 hello 的内容 `world`。巫术!我们也可以将数据从本地实例复制到远程实例: quack: #### DuckDB #2 (https://duckdb.org/2026/05/12/quack-remote-protocol#duckdb-2-1) `` -- Step one CREATE TABLE remote.hello2 AS FROM VALUES ('world2') v(s); `` 同样,你应该在 DuckDB #1 的输出中看到 `world2`。显然,这些是我们能想到的最基本的例子。表格可以复杂得多,查询可以复杂得多,数据量可以非常庞大(见下文)。还有一种方法可以通过 `query` 函数将整个逐字查询发送到远程侧,这对于大型数据集上的非常复杂的查询更好,并提供对确切执行内容的更多控制: quack: #### DuckDB #2 (https://duckdb.org/2026/05/12/quack-remote-protocol#duckdb-2-2) `` FROM remote.query( 'SELECT s FROM hello' ); `` 当然,这里还有更多内容可见。请参阅我们的文档 (https://duckdb.org/docs/current/quack/overview.html) 以获取更多详细信息。 ## 协议设计 (https://duckdb.org/2026/05/12/quack-remote-protocol#protocol-design) ### 基于 HTTP (https://duckdb.org/2026/05/12/quack-remote-protocol#http-based) Quack 直接构建在久负盛名的 HTTP,即超文本传输协议之上。从其在 CERN 的 humble 开端,HTTP 已经成为 TCP 之上所有事物的事实协议层。整个堆栈经过优化,以有效传输 HTTP 消息流。如果实现得当,该协议的开销 surprisingly 低。每个人及其弟弟都知道如何处理负载均衡、身份验证、防火墙、入侵检测等中的 HTTP。在 2026 年不在 HTTP 之上构建数据库协议将是相当误导性的。HTTP 还允许 DuckDB-Wasm 分发原生地说 Quack (https://duckdb.org/docs/current/quack/setup/quack_wasm.html)!因此,在浏览器中运行的 DuckDB 可以直接连接到在 EC2 服务器上运行的 DuckDB 实例。 ### 请求-响应模式 (https://duckdb.org/2026/05/12/quack-remote-protocol#request-response-pattern) Quack 上的交互始终由客户端驱动,采用请求-响应模式。Quack 消息例如连接请求,以 token 进行身份验证,如上所示。详见下文如何处理身份验证和授权。后续消息是执行查询并返回第一部分的响应,以及后续获取消息以检索大型结果,可能来自多个线程并行。 ### 序列化 (https://duckdb.org/2026/05/12/quack-remote-protocol#serialization) 请求和响应使用新的 MIME 类型 application/duckdb 编码。这种编码利用 DuckDB 的内部高效序列化原语用于复杂结构如数据类型和结果集。多年来,我们一直在例如预写日志 (WAL) 文件中使用相同的原语,这意味着它们相当优化且经过实战测试。 ### 加密 (https://duckdb.org/2026/05/12/quack-remote-protocol#encryption) 虽然我们想要 Quack “只需工作”,但我们也对将数据库直接连接到邪恶互联网的安全噩梦保持警惕,正如以前发生过的那样。这就是为什么 Quack 将在服务器启动时默认生成随机身份验证 token,然后必须提供给客户端。此外,Quack 服务器将默认仅绑定到 localhost(当然可以覆盖)。Quack 默认不使用 SSL,因为仅为 localhost 通信带来所有基础设施和添加依赖项有点愚蠢。我们不建议直接开放 DuckDB Quack 端点到 Internet。相反,我们强烈建议如果你选择将 Quack 暴露到万维网,使用常见的 HTTP 端点如 nginx (https://nginx.org/) 并让该代理终止 SSL(例如,使用 Let's Encrypt)。Quack 客户端将假设非本地连接启用了 SSL,这可以覆盖。我们在文档中提供了指南 (https://duckdb.org/docs/current/quack/setup/reverse_proxy.html)。 ### 往返次数 (https://duckdb.org/2026/05/12/quack-remote-protocol#round-trips) 我们仔细优化了查询的协议往返次数或请求/响应对。一旦连接,查询可以完全通过单个往返处理。这对于延迟敏感环境是一个关键优化。同时,我们严重优化了 Quack 以高效批量响应传输。据我们所知,Quack 目前是通过 socket 推送表格的最快方式,数百万行可以在几秒钟内传输。以下是一些基准测试结果。 ### 身份验证和授权 (https://duckdb.org/2026/05/12/quack-remote-protocol#authentication-and-authorization) 数据库查询的身份验证和授权是无尽的喜悦和复杂来源。我们很可能无法捕捉每个人的用例,肯定不在第一个版本中。因此,聪明的事是不尝试。对于 Quack,我们选择了与 DuckDB 可扩展性哲学相结合的身份验证模型。已经有数百个 DuckDB 扩展。Quack 带有默认身份验证方法且无授权限制,但两者都可以通过用户提供的代码覆盖。如你所见,Quack 服务器在启动时生成默认随机身份验证 token。当客户端连接时,它提供身份验证字符串。服务器端将调用身份验证回调。默认情况下,它将比较客户端提供的 token 与之前随机生成的那个。但此回调可以通过配置更改!你可以带来你自己的身份验证函数,例如查询 LDAP 目录,读取文本文件,或者只是掷骰子。由你决定。类似地,可以更改授权函数。默认授权函数对所有内容都说“是”,但你可以检查客户端尝试执行的每个查询,将查询与之前使用的身份验证字符串关联等。这些回调甚至可以是纯 SQL 宏!请参阅我们的文档以获取更多详细信息。 ### 默认端口 (https://duckdb.org/2026/05/12/quack-remote-protocol#default-port) 默认情况下,Quack 服务器监听端口 `9494`,数字 `94` 易于记住,因为 Netscape Navigator (https://en.wikipedia.org/wiki/Netscape_Navigator) 发布的那一年。 ## 基准测试 (https://duckdb.org/2026/05/12/quack-remote-protocol#benchmarks) 我们设置了两个基准测试来展示 Quack 协议。这些基准测试在运行 Ubuntu 的 Arm AWS 虚拟机上运行。我们选择了 m8g.2xlarge (https://instances.vantage.sh/aws/ec2/m8g.2xlarge) 实例类型,它有 8 个 vCPU 和 32 GB 的 RAM,重要的是,“高达 15 Gbps”的网络带宽。我们重现了一个现实世界场景,其中客户端和服务器在同一个数据中心,但在不同的机器上。我们确保两个实例在同一个“可用区”。实例之间的 ping 时间平均约为 0.280 毫秒。 ### 批量传输 (https://duckdb.org/2026/05/12/quack-remote-protocol#bulk-transfer) 第一个基准测试测试批量传输,即相当大数量的行应通过数据库协议传输的情况。如果你阅读了我们上面链接的论文,你知道这是传统数据库协议挣扎的情况。我们将 Quack 与两个系统进行比较:广泛使用的 PostgreSQL 协议和较新的 Arrow Flight SQL 协议。Arrow Flight 由 GizmoSQL (https://docs.gizmosql.com/#/) 服务器提供,该服务器也内部使用 DuckDB。我们传输 TPC-H lineitem 表中不断增加的行数,一直到惊人的 6000 万行(CSV 格式为 76 GB!),并报告 5 次运行的中位墙上时钟时间。我们期望现代面向批量的协议远远超越 PostgreSQL 协议。以下是结果: 批量传输操作运行时间(越低越好) 批量传输性能批量传输性能 你想以表格形式查看结果吗?点击此处。| 行 | DuckDB Quack | Arrow Flight | PostgreSQL | |---|---|---|---| | 100k | **0.07 s** | **0.07 s** | 0.20 s | | 1M | **0.24 s** | 0.38 s | 2.20 s | | 10M | **0.89 s** | 2.90 s | 25.64 s | | 60M | **4.94 s** | 17.40 s | 158.37 s | 我们可以看到 Quack 在批量结果集传输方面表现良好,在不到 5 秒内传输 6000 万行!即使是专用目的的 Arrow Flight SQL 协议也无法在此竞争,而 Postgres 的行基础协议总体上相当无望。 公平地说,我们必须提到标准 PostgreSQL 客户端不会跨多个线程并行化读取,但 Quack 和 Arrow 可以。无耻的广告:DuckDB 的 PostgreSQL 客户端 (https://duckdb.org/docs/current/core_extensions/postgres.html) 在某些情况下也可以这样做! ### 小写入 (https://duckdb.org/2026/05/12/quack-remote-protocol#small-writes) 第二个基准测试测试小型追加。这是一个常见的用例,例如,将可观测性数据集中在单个中央 DuckDB 实例中。这以不同方式压力测试数据库协议,例如,客户端和服务器之间的多个往返以完成单个交易将是一个劣势。我们通过创建与 TPC-H lineitem 表具有相同结构的空表,然后向其插入随机值,每行在其自己的 `INSERT` 交易中,来测试这一点。

相似文章

DuckDB: it's not quack science

Lobsters Hottest

DuckDB是一个开源嵌入式分析型数据库,支持直接查询文件、嵌入应用,并提供友好的SQL扩展,在数据分析场景下比传统Unix管道更高效。