来源:https://duckdb.org/2026/05/29/new-iceberg-features.html
DuckDB-Iceberg 在 v1.5.3 中的新特性
作者: Tom Ebergen, Thijs Bruineman
日期: 2026-05-29
阅读时间: 5 分钟
摘要: DuckDB-Iceberg 现在拥有许多新特性来支持 Iceberg 表和 Iceberg REST Catalog,包括:MERGE INTO、ALTER TABLE、分区转换(partition transforms)、V3 支持等!
尽管开发 DuckLake v1.0 和 Quack 所需的功能需要大量工作,DuckLabs 团队仍在努力开发 DuckDB-Iceberg 扩展。在这篇博文中,我们将演示 DuckDB v1.5.3 中可用的一些功能。其中许多功能在我们上一篇以 Iceberg 为主题的博文"DuckDB-Iceberg 中的写入操作"中被指定为未来版本发布的功能------你可以将本文视为那篇博文的"第 2 部分"。
入门指南
要体验新的 DuckDB-Iceberg 功能,你需要连接到常用的 Iceberg REST Catalog。有很多方法可以做到这一点:请查看"连接到 REST Catalog"页面,其中包含连接到 Apache Polaris 和 Lakekeeper 等 catalog 的说明。如果你想连接到 Amazon S3 Tables,请查阅"连接到 S3 Tables"页面。无论如何,你的 ATTACH 命令看起来会像这样:
sql
ATTACH 'warehouse_name' AS my_datalake (
TYPE iceberg,
other options
);
MERGE INTO 支持
当目标表没有主键时(所有数据湖仓格式都是这种情况),DuckDB 的 MERGE INTO 语句是表达 upsert 操作的推荐方式。从 v1.5.3 开始,MERGE INTO 现已完全支持 Iceberg 表。你可以在一条语句中对 Iceberg 表应用一个变更集,按行决定是插入、更新还是删除。
我们以这张表为例:
sql
CREATE TABLE my_datalake.default.people (
id INTEGER,
name VARCHAR,
salary FLOAT
);
INSERT INTO my_datalake.default.people
VALUES (1, 'John', 92_000.0), (2, 'Anna', 100_000.0);
┌───────┬─────────┬──────────┐
│ id │ name │ salary │
│ int32 │ varchar │ float │
├───────┼─────────┼──────────┤
│ 1 │ John │ 92000.0 │
│ 2 │ Anna │ 100000.0 │
└───────┴─────────┴──────────┘
让我们对该表运行一次更新,包含两条记录,一条增加 ID 为 1 的人的薪水,另一条添加一个 ID 为 3 的新人。
sql
MERGE INTO my_datalake.default.people AS target
USING (
FROM (VALUES
(1, 'John', 105_000.0),
(3, 'Sarah', 95_000.0)
) t(id, name, salary)
) AS upserts
ON (upserts.id = target.id)
WHEN MATCHED THEN UPDATE
WHEN NOT MATCHED THEN INSERT;
查询结果时,我们得到以下结果:
sql
SELECT *
FROM my_datalake.default.people
ORDER BY id;
┌───────┬─────────┬──────────┐
│ id │ name │ salary │
│ int32 │ varchar │ float │
├───────┼─────────┼──────────┤
│ 1 │ John │ 105000.0 │
│ 2 │ Anna │ 100000.0 │
│ 3 │ Sarah │ 95000.0 │
└───────┴─────────┴──────────┘
你也可以将匹配(MATCHED)和不匹配(NOT MATCHED)分支与 WHEN MATCHED THEN DELETE 组合起来,在同一个语句中表达一个删除集。与 UPDATE 和 DELETE 一样,MERGE INTO 使用读时合并(merge-on-read)语义,并将位置删除(positional deletes)写入 Iceberg 表。
ALTER TABLE 支持
在 DuckDB v1.4 的 Iceberg 扩展中,Iceberg 表缺乏模式演化(schema evolution)是一个已知的限制。在 v1.5.3 中,ALTER TABLE 语句现已支持 Iceberg 表,涵盖了最常见的模式演化操作。
sql
-- 创建表
CREATE TABLE my_datalake.default.simple_table AS
FROM (VALUES
(1, 'Andy'),
(2, 'Bob'),
(3, 'Claire'),
(4, 'Mr. Duck')) t(col1, col2);
-- 重命名表
ALTER TABLE my_datalake.default.simple_table
RENAME TO renamed_table;
-- 添加列
ALTER TABLE my_datalake.default.renamed_table
ADD COLUMN col3 DOUBLE;
-- 重命名列
ALTER TABLE my_datalake.default.renamed_table
RENAME COLUMN col2 TO name;
-- 删除列
ALTER TABLE my_datalake.default.renamed_table
DROP COLUMN col3;
-- 设置格式版本
ALTER TABLE my_datalake.default.renamed_table
SET ('format-version' = 3);
在模式更改后查询该表,我们得到以下结果:
sql
SELECT *
FROM my_datalake.default.renamed_table
ORDER BY col1;
┌───────┬──────────┐
│ col1 │ name │
│ int32 │ varchar │
├───────┼──────────┤
│ 1 │ Andy │
│ 2 │ Bob │
│ 3 │ Claire │
│ 4 │ Mr. Duck │
└───────┴──────────┘
在后台,每个 ALTER TABLE 语句都会更新 Iceberg 表的 current-schema-id。这些更改在其他能够识别 Iceberg 的引擎下次查询 LoadTableInformation 端点时就会变得可见。Iceberg 模式演化仅涉及元数据,因此不会重写任何数据文件。
truncate 和 bucket 支持
Iceberg 规范定义了多种分区转换(partition transforms),用于确定数据文件在磁盘上的布局方式。在 v1.5.3 中,DuckDB-Iceberg 支持创建、插入和更新使用 bucket 和 truncate 分区转换的表。
bucket(N, col) 转换将列的值哈希到 N 个桶中,这在你想对高基数列进行稳定分区时非常有用。truncate(W, col) 按前 W 个字符对行进行分组(对于数值列,则按向下舍入到 W 的倍数进行分组),这对于基于前缀的分区非常有用。
sql
CREATE TABLE my_datalake.default.events (
event_id BIGINT,
user_id BIGINT,
country VARCHAR,
payload VARCHAR
)
PARTITIONED BY (bucket(16, user_id), truncate(2, country));
INSERT INTO my_datalake.default.events
VALUES
(1, 1001, 'United States', 'click'),
(2, 1002, 'United Kingdom', 'view'),
(3, 1003, 'Germany', 'click'),
(4, 1004, 'Netherlands', 'view');
你可以检查生成的数据文件以验证分区:
sql
SELECT file_path, record_count
FROM iceberg_metadata(my_datalake.default.events)
WHERE content = 'EXISTING';
针对使用 bucket 和 truncate 分区的表的更新和删除操作也得到支持,在读时合并语义下使用位置删除。
Iceberg 模式属性
Iceberg catalog 允许在模式(namespace)级别附加任意的键值属性。这些属性通常用于记录所有权、描述、默认存储位置或任何适用于模式中每个表的其他元数据。
iceberg_schema_propertiesset_iceberg_schema_propertiesremove_iceberg_schema_properties
你可以如下使用它们:
sql
-- 设置模式属性
CALL set_iceberg_schema_properties(my_datalake.default, {
'owner': 'analytics-team',
'description': 'Default analytics schema'
});
-- 读取模式属性
SELECT * FROM iceberg_schema_properties(my_datalake.default);
┌─────────────┬──────────────────────────┐
│ key │ value │
│ varchar │ varchar │
├─────────────┼──────────────────────────┤
│ owner │ analytics-team │
│ description │ Default analytics schema │
└─────────────┴──────────────────────────┘
-- 删除模式属性
CALL remove_iceberg_schema_properties(
my_datalake.default,
['description']
);
模式属性通过 Iceberg REST Catalog 写入,因此任何连接到同一 catalog 的其他能够识别 Iceberg 的引擎都会立即看到更新。返回的值是剩余模式属性的数量。
V3 支持
Iceberg v3 规范引入了几个新特性,DuckDB-Iceberg 现在对这些特性同时支持读取和写入:
VARIANT和TIMESTAMP_NS数据类型- 列的模式级默认值
- 二进制删除向量
- 行血缘追踪
实际中最大的变化是二进制删除向量。在 v2 表中,DuckDB-Iceberg 将位置删除写入 Parquet 文件;在 v3 表中,相同的信息被编码为更紧凑的二进制删除向量(Puffin 文件)。DuckDB 会根据表的 format-version 自动选择正确的格式。
你可以通过在创建表时设置 format-version 表属性来创建一个 v3 表:
sql
CREATE TABLE my_datalake.default.v3_table
WITH ('format-version' = 3) AS
FROM (VALUES
(1, {'kind': 'click', 'x': 10}::VARIANT, TIMESTAMP_NS '2026-05-20 12:00:00.123456789'),
(2, {'kind': 'view'}::VARIANT, TIMESTAMP_NS '2026-05-20 12:00:00.987654321')
) t(id, payload, event_time);
-- 针对 v3 表的删除操作会写入二进制删除向量
DELETE FROM my_datalake.default.v3_table
WHERE id = 1;
SELECT * FROM my_datalake.default.v3_table;
┌───────┬──────────────────┬───────────────────────────────┐
│ id │ payload │ event_time │
│ int32 │ variant │ timestamp_ns │
├───────┼──────────────────┼───────────────────────────────┤
│ 2 │ {"kind": "view"} │ 2026-05-20 12:00:00.987654321 │
└───────┴──────────────────┴───────────────────────────────┘
查看表的元数据可以确认删除操作是作为删除向量而不是位置删除 Parquet 文件写入的:
sql
SELECT manifest_content, content, file_format
FROM iceberg_metadata(my_datalake.default.v3_table);
┌──────────────────┬──────────────────┬─────────────┐
│ manifest_content │ content │ file_format │
│ varchar │ varchar │ varchar │
├──────────────────┼──────────────────┼─────────────┤
│ DATA │ EXISTING │ parquet │
│ DELETE │ POSITION_DELETES │ puffin │
└──────────────────┴──────────────────┴─────────────┘
注意:DuckDB-Iceberg 目前还不支持 Geography 类型和 Unknown 类型;我们计划在 DuckDB v2.0.0 中添加它们。
结论与未来工作
通过这些功能,DuckDB-Iceberg 已经填补了上一篇博文中提到的许多空白:分区写入、模式演化、MERGE INTO 以及许多 Iceberg v3 功能现已可用。未来还有更多功能将要实现,一如既往,如果您希望某个特定功能得到优先处理,请通过 DuckDB-Iceberg GitHub 仓库与我们联系,或直接与我们的工程师沟通。