PostgreSQL码农集散地

DuckLake v1.0 发布:基于 SQL 的湖仓一体格式正式量产

数据工程师的"终极懒人包"来了:不用管文件碎片、不用写迁移脚本,一个格式通吃所有引擎——DuckLake v1.0 正式发布


2025 年,当整个数据圈还在 Delta Lake 和 Iceberg 之间反复横跳、为一个"该选哪个湖格式"吵得不可开交的时候,DuckDB 团队悄悄上线了一份宣言,开口就是一句暴击: "你们的元数据管理方式,从根儿上就错了。"

这句话的杀伤力有多大?不到一年,一个全新的湖仓格式——DuckLake——在 GitHub 上狂揽数千 Star,跻身 DuckDB 十大核心扩展之一,数十家公司在生产环境里用它跑了真实业务,甚至 O'Reilly 都开始给它写书了。

而就在今天,DuckLake v1.0 正式发布——带着一份稳定的规范、一套功能完整且性能彪悍的参考实现,以及一份让人看了就想动手的 Roadmap。


TL;DR

我们正式发布 DuckLake v1.0,这是一套基于 SQL 构建的生产级湖仓格式规范。其参考实现——集成在 DuckDB 中的 ducklake 扩展——已在 DuckDB v1.5.2 中同步发布,即刻可用。


先说说为什么这个项目让人"上头"

如果你刚刷到这篇文章,可能在想:DuckLake 到底是什么?

简单来说,DuckLake 是一种湖仓格式(lakehouse format) ,让你把数据存在对象存储里,同时能像操作数据库一样访问它——类似于 Delta Lake + Unity Catalog 或 Iceberg + Lakekeeper 的组合。

但它有一个根本性的不同:

其他格式把元数据散落在对象存储的各个文件里,而 DuckLake 把所有元数据都存在一个数据库里——我们叫它"目录"(catalog)。

这个 catalog 可以是任何支持 SQL、有主键、能持久化存储的数据库系统。DuckLake 规范定义了每个版本需要的元数据表、支持的数据类型,以及如何检索表元数据来操作湖仓。

他们还基于 DuckDB 引擎做了完整实现——ducklake 扩展,支持三种主流 catalog: SQLite、PostgreSQL 和 DuckDB(没错,DuckDB 本身就能当 catalog)。

过去一年里,DuckDB 团队在内部和社区的共同推动下,持续完善规范:支持在不深度复制的情况下引入已有 Parquet 文件、引入 Iceberg 兼容性、上线 Geometry 和 Variant 类型支持,还发布了从 DuckDB 迁移到 DuckLake 的详细指南和迁移脚本。


它解锁了哪些有意思的场景?

数据流式入湖:利用 Data Inlining 特性(用 catalog 数据库暂存小批量更新),可以直接把流式数据写入数据湖,而不用担心文件碎片问题。

零基础设施湖仓:只需要一块存储和一个公网 HTTPS 端点,就能搭建一个只读的湖仓服务——不需要任何认证系统。

多用户协作:在 DuckDB 生态里,DuckLake 解锁了一种"多人联机"模式——多个 DuckDB 实例同时访问同一个 DuckLake,通过一个中心化的 PostgreSQL catalog 数据库协调。


社区的反应,属实"炸裂"

DuckLake 的社区采纳速度超出了所有人的预期:

  • ducklake 扩展目前是 DuckDB 下载量 Top 10 的核心扩展之一
  • 已有多个语言的客户端:Apache DataFusion、Hadoop 生态(Apache Spark via MotherDuck)、Trino、Pandas DataFrame(大部分由 AI 代码生成)
  • MotherDuck 提供托管版 DuckLake 服务,连 catalog 数据库和数据存储都帮你托管好了
  • 数十家公司已在生产环境使用——新官网首页甚至专门做了一个 Logo 墙展示
  • O'Reilly 官方书籍正在撰写中——这在开源数据项目中极为罕见

💡 想看更多使用案例和工具库?去 awesome-ducklake 仓库逛逛。


DuckLake v1.0 核心功能一览

以下所有功能均已在今天同步发布的 DuckDB v1.5.2 中可用。

1. Data Inlining(小文件问题的终结者)

Data Inlining 是 DuckLake 的旗舰功能之一。它的核心思路是: 把小的 INSERT、DELETE、UPDATE 操作直接写进 catalog 数据库,而不是在对象存储里生成一堆小文件。

v1.0 带来了完整的 UPDATE 和 DELETE 的 Inlining 支持,且该功能现已默认开启,默认阈值为 10 行。

CREATETABLE lake.t (idINT, statusVARCHAR);
INSERTINTO lake.t VALUES (1, 'en route'), (2, 'shipped');
DELETEFROM lake.t WHEREid = 1;
UPDATEFROM lake.t SETstatus = 'delivered'WHEREid = 2;
FROM ducklake_list_files('lake', 't'); -- 返回空——数据在哪?
CHECKPOINT; -- 才写入对象存储

看到了吗?中间那一堆增删改操作,完全没有产生任何新文件。CHECKPOINT 之后才一次性刷到对象存储。

2. Sorted Tables(排序表:让读取飞起来)

如果你的查询经常在某个高基数字段(如 ID 或时间戳)上做过滤,给它排个序能显著提升读取性能。行组剪枝(row group pruning)和文件剪枝(file pruning)都会从中受益。

Sorted Tables 不仅支持列排序,还支持任意 SQL 表达式。默认情况下,排序会在 Compaction、Flush 或写入时自动触发——但写入时的自动排序可以禁用,避免影响写入性能。

CREATETABLE lake.sorted_t (idINT, payload JSON);
ALTERTABLE lake.sorted_t SET SORTED BY (idASC);
INSERTINTO lake.sorted_t VALUES
    (33, '{"key": "value"}'),
    (2,  '{"key": "value"}'),
    (42, '{"key": "value"}'),
    (1,  '{"key": "value"}');
CHECKPOINT;
FROM lake.sorted_t;

3. Bucket Partitioning(哈希分桶:跳过全表扫描)

分桶通过哈希函数将目标列的值映射到指定桶数,适合高基数字段——既享有分区的部分收益,又不受基数爆炸的困扰。实现上使用 Murmur3 哈希,与 Iceberg 完全兼容。

CALL lake.set_option('data_inlining_row_limit', 0);
CREATETABLE lake.events (user_name VARCHAR, event_type VARCHAR, ts TIMESTAMP);
ALTERTABLE lake.events SET PARTITIONED BY (bucket(8, user_name));
INSERTINTO lake.events VALUES
    ('alice', 'click', '2024-01-01'),
    ('bob', 'view', '2024-01-01'),
    ('charlie', 'click', '2024-01-02');
EXPLAINANALYZEFROM lake.events WHERE user_name = 'alice';

4. Geometry 类型:终于支持地理空间数据了

紧跟 DuckDB 核心对 GEOMETRY 类型的支持,DuckLake 也带来了更强大的地理统计能力——支持过滤下推(filter pushdown) 。GEOMETRY 类型现在可以嵌套在 struct、list 和 map 中。

使用 && 操作符可以直接利用边界框剪枝,连复杂的几何运算都不用跑全表:

LOAD spatial;
CALL lake.set_option('data_inlining_row_limit', 0);
CREATETABLE lake.places (nameVARCHAR, location GEOMETRY);
INSERTINTO lake.places VALUES ('Amsterdam', ST_Point(4.9, 52.37));
INSERTINTO lake.places VALUES ('London', ST_Point(-0.12, 51.51));
SELECTnameFROM lake.places
WHERE location && ST_GeomFromText('POLYGON((4 52, 5 52, 5 53, 4 53, 4 52))');
-- 返回 Amsterdam

5. Variant 类型:JSON 的"进化版",它来了

Variant 是 DuckDB 团队押注的下一代半结构化数据类型,相比 JSON 有三大核心优势:

特性
JSON
Variant
支持类型
有限(string/number/boolean等)
丰富(DATE、TIMESTAMP 等均可)
存储格式
字符串
二进制编码
类型展开
不支持
支持 shred 到原始类型,查询性能大幅提升

DuckDB 团队甚至断言: Variant 最终会取代 JSON 成为数据库中处理半结构化数据的主流类型。

CREATETABLE lake.events (idINT, payload VARIANT);
INSERTINTO lake.events VALUES
    (1, '{"user": "alice", "ts": "2024-01-01"::{TIMESTAMP}}),
    (2, '
{"user": "bob", "ts": "2024-01-02"::{TIMESTAMP}, "rand": "value"}');
SELECT * FROM lake.events WHERE payload.user = '
bob';

6. Deletion Vectors(删除向量):Iceberg v3 同款特性

Deletion Vectors 是 Iceberg v3 规范引入的核心特性。DuckLake v1.0 已实现基于 Puffin 文件存储的删除向量,与 Iceberg 在数据层面保持兼容。这种删除向量与传统的 delete file 机制作用相似。

⚠️ 该功能目前为实验性质,团队正在规划 v1.1 中的改进。

CREATETABLE lake.t (idINTEGER);
CALL lake.set_option('write_deletion_vectors', true, table_name => 't');
INSERTINTO lake.t FROMrange(100);
DELETEFROM lake.t WHEREid < 5;

稳定背后:108 个 PR 的硬核打磨

v1.0 的发布不是一蹴而就的。官方透露,自确定 v1.0 目标以来,共合并了 108 个 PR:

  • 🔧 68 个 PR 聚焦可靠性和正确性(Bug 修复)
  • ⚙️ 12 个 PR 做了内部重构,为未来功能打基础
  • 🚀 12 个 PR 专注性能优化

DuckLake 的未来:v1.1 和 v2.0 的路线图

DuckLake v1.1(近在眼前)

两个主要功能需要规范变更:

① Variant Inlining:目前 Variant inlining 仅在原生支持 Variant 的 catalog(如 DuckDB)中工作。当 catalog 不支持 Variant 类型时,表永远无法被 inlined。v1.1 将设计并实现相应的规范扩展。

② 多删除向量 Puffin 文件:v1.0 已支持单个删除向量 Puffin 文件,v1.1 将支持在单个 Puffin 文件中存储和读取多个删除向量,在保留时间旅行能力的同时,最大程度减少小文件问题。

DuckLake v2.0(长期愿景)

v2.0 不会很快到来,团队将优先专注于成熟化现有功能集并确保规范的稳定性。目前规划的方向包括:

  • 类 Git 的分支管理:创建 DuckLake 的分支,并在分支间合并变更——数据版的 Git
  • 基于权限的角色系统:定义角色和权限来控制对 DuckLake 对象和操作的访问(目前可通过 PostgreSQL + S3 配置实现,但团队想让这变得更简单)
  • 增量物化视图:支持可以跟踪 DuckLake 表变更并增量刷新的物化视图,而无需全量重算

结语

今天,DuckLake v1.0 正式发布——一套稳定的规范,配合兼容该版本的 ducklake 扩展,一起交付到开发者手中。

短短一年,DuckLake 从一份"反常识"的宣言出发,成长为一个被社区广泛采纳、有真实生产案例、甚至 O'Reilly 都来写书的成熟项目。这背后是 DuckDB 团队和社区的共同努力。


想了解更多技术细节或直接上手体验?访问 DuckLake 官网 或查看 awesome-ducklake 获取所有生态工具。


原文:DuckLake v1.0: The Lakehouse Format Built on SQL Reaches Production-Readiness,The DuckDB Team,2026-04-13