一、AWS 买下的是公司,不是 DuckDB

就在这周,AWS 宣布已签署最终协议,收购位于阿姆斯特丹的 DuckLabs——DuckDB 背后的公司。DuckLabs 团队将整体加入 AWS。 DuckDB 本身继续留在独立的非营利组织 DuckDB Foundation 名下,继续以 MIT 协议免费开源;DuckDB 的创造者、DuckLabs 联合创始人 Hannes Mühleisen 和 Mark Raasveldt 将继续带领团队,并继续主导开源项目的技术方向。

DuckDB 是一个进程内的列存分析型数据库,常被称作「分析领域的 SQLite」。它没有服务端,不需要独立部署,以库的形式被宿主程序链接进去。而正是这个形态,让它走出了一条和其他分析引擎都不同的路。

二、PostgreSQL 的 OLAP 短板,与 pg\_duckdb 的诞生先说问题

PostgreSQL 是典型的行存 OLTP 引擎,它在事务处理上非常强,但跑大规模聚合、全表扫描、大 JOIN 时,比不过列存向量化引擎——这不是实现好坏的问题,是存储布局和执行模型决定的。

长期以来这个缺口靠 ETL 填:把数据同步一份到外部数仓,交易归交易,报表归报表。这也意味着数据副本、同步延迟。

而 DuckDB 恰好是列存向量化引擎里最特别的一个:它是进程内的,以库的形式存在,没有服务端、不需要独立部署。一个不需要独立部署的分析引擎,天然就适合被塞进另一个数据库里。这个想法并不难想到,难的是把它做出来。

pg\_duckdb 项目起源

2024 年 8 月,一个把 DuckDB 嵌入 PostgreSQL 的开源协作项目公开亮相,参与方是三家:Hydra、DuckDB Labs、MotherDuck​。项目最初由 Hydra 发起,他们此前一直在做 PostgreSQL 上的分析扩展,贡献了 PG 扩展与存储方面的工程经验;DuckDB Labs 提供引擎侧的支持;MotherDuck 则把它接进了自己的云服务。

三家凑到一起并不是巧合——它们面对的是同一个需求:Hydra 要给 PG 用户提供分析能力,MotherDuck 要让 DuckDB 能对接存量的 PG 数据,DuckDB Labs 需要一条通往 OLTP 世界的入口。各做各的,不如合做一个。

项目最终落在了 DuckDB 自己的 GitHub 组织下——github.com/duckdb/pg_duckdb。这就是「官方扩展」的实际含义:不是一个第三方项目被认可,而是它本身就归属在 DuckDB 组织内维护。

两个项目是什么关系

总结来说就是DuckDB 是引擎,pg\_duckdb 是引擎的外壳。

  • DuckDB 以 git submodule 的形式被引入(third_party/duckdb,指向 github.com/duckdb/duckdb.git),版本在 Makefile 里被钉死(DUCKDB_VERSION,随上游主线滚动升级),编译后与扩展代码一起链进同一个 .so。
  • 运行时,DuckDB 与 PostgreSQL backend ​同进程、同地址空间​。没有网络、没有 IPC、没有独立进程,CREATE EXTENSION pg_duckdb 之后,每个 backend 内部就多了一个可用的分析引擎。
  • 依赖方向是单向的:DuckDB 完全不知道 pg\duckdb 的存在,可以独立使用;pg\duckdb 离开 DuckDB 则无法存在。

分工也很清晰:事务、权限、catalog、存储、连接管理仍然全部归 PostgreSQL;DuckDB 只负责列存向量化执行。用户侧的开关很简单:

SET duckdb.force_execution = true;

之后不改一行 SQL,符合条件的查询就会走 DuckDB 执行;不支持的查询自动回退到 PostgreSQL 原生执行器。

三、pg\_duckdb 是怎么实现的

理解 pg\_duckdb 的实现,最关键的一点是:它不是把 PostgreSQL 的计划树翻译成 DuckDB 的计划树,而是把查询反解析成 SQL 文本,交给 DuckDB 重新解析。

这个「SQL 文本往返」是整个架构的核心,也是后面所有适配工作的根源。完整链路如下:

1. Hooks 拦截查询

_PG_init 时接管三个 hooks(src/pgduckdb_hooks.cpp):planner_hook、ExecutorStart_hook、ExplainOneQuery_hook。查询进入规划阶段时先过一遍判定:是否开启了 force_execution、是否引用了 DuckDB 才有的表函数(read_parquet 等)、是否命中不支持的类型或语法。不合格就原样交还 standard_planner,用户全程无感知。

2. 反解析成 DuckDB SQL

命中之后,pgduckdb_planner.cpp 调用 pgduckdb_get_querydef()(src/pgduckdb_ruleutils.cpp,是 PostgreSQL pg_get_querydef 的定制版本),把 Query 树重新渲染成一段 SQL 字符串,再交给 DuckDB 的 Connection::Prepare()。DDL 路径类似,pgduckdb_get_tabledef() 负责把 PG 表定义渲染成 DuckDB 的 CREATE TABLE。

3. 用 CustomScan 节点承载执行

规划结果是一个 CustomScan 节点(src/pgduckdb_node.cpp),实现了 BeginCustomScan / ExecCustomScan / EndCustomScan / ExplainCustomScan 一整套回调。对 PostgreSQL 的执行器而言,DuckDB 只是一个普通的扫描节点:每次 ExecCustomScan 从 DuckDB 拉一批结果,转成 TupleTableSlot 往上吐。这也是 EXPLAIN 能同时展示两边计划的原因。

4. 反向读回 PostgreSQL 堆表

数据不需要导出。DuckDB 侧通过 replacement scan 机制注册了对 PG 表的访问(src/scan/postgres_scan.cpp、postgres_table_reader.cpp):当 DuckDB 解析到一个它 catalog 里没有的表名时,回调进 pg\_duckdb,由后者直接走 PostgreSQL 的缓冲区管理器和快照读堆表。过滤条件会被下推回 PG 侧扫描(ExtractQueryFilters),并且支持拉起 PostgreSQL 并行工作进程来喂数据。

5. 类型双向映射

src/pgduckdb_types.cpp 负责两个方向的转换:PG OID → DuckDB LogicalType,以及 DuckDB 值 → PG Datum。这是整个扩展里最琐碎也最容易出问题的一块——两边的类型系统并不同构(典型如无精度约束的 NUMERIC,默认会告警回退)。

另外还有 src/catalog/ 下的一组文件,让 DuckDB 的事务管理器与 PostgreSQL 的事务边界对齐;src/vendor/ 则是从 PostgreSQL 和 DuckDB 内部抄出来的头文件——这也说明了两个项目的耦合有多紧:DuckDB 的 C++ 内部 API 并不承诺稳定,每次升级 DuckDB 版本,pg\_duckdb 往往都要跟着改代码。

四、IvorySQL 的工作:ivy\_duckdb

IvorySQL 是基于 PostgreSQL 的 Oracle 兼容数据库,在内核里实现了一整套 Oracle 兼容的数据类型和语义。这些类型是 IvorySQL 自己注册的,OID 是动态分配的——而 pg\duckdb 的类型映射大量依赖硬编码的内建 OID 判断。直接拿上游 pg\duckdb 用在 Oracle 模式下,结果就是:要么大面积回退、失去加速,要么在类型转换路径上直接报错。

于是有了 ivy_duckdb(https://github.com/IvorySQL/ivy\duckdb ),IvorySQL 维护的 pg\duckdb 分支。当前基线是上游 pg\_duckdb 1.1.0 + DuckDB v1.4.3,相对上游约 2500 行改动,集中在 src/、include/、test/ 三处。主要工作分成四块:

1. 动态 OID 的类型识别与缓存

新增 Ivory*Oid() 一族接口(include/pgduckdb/pgduckdb_metadata_cache.hpp),覆盖 IvorySQL 中实现的 Oracle 兼容类型,通过 catalog 查找拿到运行时 OID 并缓存起来,随 catalog 失效自动更新。类型映射的判据从「硬编码 OID」改成「查缓存」,这是所有后续工作的地基。

2. 兼容「SQL 文本往返」

  • alias 扩展​。ivy 类型在 DuckDB 侧以 alias 形式承载(如 TIMESTAMP 带 alias ivory:oradate),PG 侧重新解析时在 postgres_scan.cpp 里做 alias 剥离后再渲染。
  • 类型名规范化​。反解析出的 oracle 类型名 DuckDB 不认识,需要在 pgduckdb_ruleutils.cpp 做文本改写 ,并通过 ExtensionLoader::RegisterType() 把全部 Oracle 兼容类型正式注册进 DuckDB 的系统 catalog。
3. 稳定性与工程化
  • 增强使用体验,增加诊断扩展状态的系统函数。
  • 支持离线构建(OFFLINE=1 关闭 httpfs 的 FetchContent 拉取,预安装 Duckdb 扩展),适配内网环境。
4. 开箱即用:ivy\_mooncake 与 docker 镜像

ivy_duckdb 本身是一个扩展,需要自行编译安装。对想快速上手的用户,更方便的入口是 ​ivy_mooncake​(https://github.com/IvorySQL/ivy\_mooncake )——IvorySQL 维护的 pg_mooncake 发行版,它把 ivy_duckdb 直接作为子模块包含在内。

pg_mooncake 本身是给 PostgreSQL 表建立 Iceberg 列存镜像的扩展:行存表照常承担 OLTP 写入,镜像表以亚秒级新鲜度同步,分析查询打在列存上,加速引擎正是 DuckDB。它的扩展控制文件里写着 requires = 'pg_duckdb'——也就是说,ivy_mooncake 天然依赖 ivy_duckdb,两者是一套东西的两层:

  • ivy_duckdb—— 查询加速层。行存表不动,符合条件的查询交给 DuckDB 执行。
  • ivy_mooncake + ivy_moonlink—— 列存镜像层。通过逻辑复制把行存表增量同步成 Iceberg 列存,分析查询直接打列存。

最快的体验方式是预览镜像,里面已经预装并预加载好了 IvorySQL + pg_duckdb + pg_mooncake:

docker run --name ivy_mooncake \-e IVORYSQL_PASSWORD=password \-p 5432:5432 -p 1521:1521 \-v ivy_mooncake_data:/var/lib/ivorysql/data \-v ivy_mooncake_warehouse:/tmp/moonlink_iceberg \registry.highgo.com/mooncake/ivy_mooncake:0.1

起来之后三条语句就能看到效果:

CREATE EXTENSION pg_mooncake CASCADE;          -- CASCADE 会一并装上 pg_duckdb-- 开启查询加速,然后查原表即可SET duckdb.force_execution = true;
五、给 IvorySQL 带来的 OLAP 能力

把上面这些工作串起来看,结论其实很简单:IvorySQL 在保持 Oracle 兼容的前提下,获得了原生的 OLAP 分析能力。

  • TP 侧不变​。行存表、事务、约束、Oracle 兼容的 PL/SQL 和数据类型,全部照旧,没有任何妥协。
  • OLAP 侧两档可选​。轻量档是 ivy_duckdb 的查询加速:SET duckdb.force_execution = true,SQL 一行不改,直接吃到列存向量化执行;重量档是 ivy_mooncake 的 Iceberg 列存镜像:给热表建一个自动同步的列存副本,分析查询打在副本上,行存侧的 OLTP 负载不受影响。

两档能力的详细用法——列存镜像的建立与同步、加速路径的开关与回退规则、Oracle 类型的支持范围——参见 IvorySQL/ivy\mooncake (https://github.com/IvorySQL/ivy\mooncake) 仓库文档。

我们在 IvorySQL 的 Oracle 兼容模式和 pg 模式下都做了验证,用 TPC-H 查询集对三条执行路径做了同机横比:行存原生执行、ivy_duckdb 查询加速、ivy_mooncake 列存镜像。结论是清晰的量级差异——查询加速相对原生执行有明显提升,列存镜像则再上一个台阶,两者的差距在大数据量、重扫描的聚合类查询上尤其突出。

六、小结

回到开头:AWS 收购 DuckLabs,改变的是 DuckDB 的资源与商业化路径,没有改变它的许可证和治理结构。真正烧到 PostgreSQL 的,不是这桩交易本身,而是它背后那个已经成立的事实——列存分析引擎可以被装进 OLTP 数据库,而且已经有人跑通了。对 PostgreSQL 生态而言,更值得关注的是 pg\_duckdb 这条已经跑通的路径:不改 SQL、不搬数据、同进程嵌入,把列存向量化能力直接补进 OLTP 数据库。

目前 ivy\mooncake/ivy\duckdb 发布的是 v0.1 预览版,欢迎大家使用。如果遇到问题,欢迎到仓库提 issue:

  • IvorySQL/ivy\mooncake (https://github.com/IvorySQL/ivy\mooncake)
  • IvorySQL/ivy\duckdb (https://github.com/IvorySQL/ivy\duckdb)

  • duckdb/pg\_duckdb — DuckDB 官方 PostgreSQL 扩展
  • IvorySQL/ivy\_duckdb — IvorySQL 的 Oracle 兼容适配分支
  • IvorySQL/ivy\mooncake — 包含 ivy\duckdb 的列存分析发行版,提供 Docker 预览镜像
  • IvorySQL/ivy\_moonlink — moonlink 的 IvorySQL 适配分支,Rust 实现的 Iceberg 实时摄取引擎,通过逻辑复制把行存表的增删改同步进列存镜像
  • AWS to acquire DuckLabs, the Amsterdam-based company behind DuckDB — AWS 官方公告
  • pg\duckdb: Splicing Duck and Elephant DNA — pg\duckdb 项目发起时的介绍