DeepSeek总结的DuckDB-Iceberg 在 v1.5.3 中的新特性

来源: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 组合起来,在同一个语句中表达一个删除集。与 UPDATEDELETE 一样,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 支持创建、插入和更新使用 buckettruncate 分区转换的表。

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';

针对使用 buckettruncate 分区的表的更新和删除操作也得到支持,在读时合并语义下使用位置删除。

Iceberg 模式属性

Iceberg catalog 允许在模式(namespace)级别附加任意的键值属性。这些属性通常用于记录所有权、描述、默认存储位置或任何适用于模式中每个表的其他元数据。

  • iceberg_schema_properties
  • set_iceberg_schema_properties
  • remove_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 现在对这些特性同时支持读取和写入:

  • VARIANTTIMESTAMP_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 仓库与我们联系,或直接与我们的工程师沟通。

相关推荐
Database_Cool_1 小时前
数据仓库弹性扩缩容怎么实现?阿里云 AnalyticDB MySQL Serverless 弹性架构详解
数据库·人工智能·阿里云
abcy0712131 小时前
django聚合函数
数据库·sqlite
念越1 小时前
数据库系统概论第6版王珊版:第二章关系代数与第三章SQL期末重点整理
数据库·sql·性能优化
TDengine (老段)1 小时前
TDengine 数据保留与 TTL — 多级存储、过期删除与分层迁移
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
安当加密03011 小时前
等保2.0三级数据库加密:2026检查清单 + TDE部署实战(附脚本)
数据库·网络安全·数据安全·tde·等保2.0·数据库加密·tde透明加密
解决问题no解决代码问题1 小时前
TiDB 原理与节点宕机实战讲解
数据库·tidb
江湖中的阿龙1 小时前
Redis 五大核心数据类型底层原理
数据库·redis·缓存
茫忙然1 小时前
Claude Code 接入 DeepSeek 或 多模型 教程(Linux)
java·linux·数据库
AI人工智能+电脑小能手10 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试