跳到主要内容
版本:4.x

Iceberg Catalog

Doris 支持通过多种元数据服务访问 Iceberg 表数据。除支持数据读取外,Doris 也支持对 Iceberg 表进行写入操作,包括 INSERT、INSERT OVERWRITE、UPDATE、DELETE 和 MERGE INTO。

使用 Docker 快速体验 Apache Doris & Iceberg

提示

用户可以通过 Hive Catalog 访问使用 Hive Metastore 作为元数据的 Iceberg 表。但依然推荐直接使用 Iceberg Catalog 以避免一些兼容性问题。

适用场景​

场景说明
查询加速利用 Doris 分布式计算引擎,直接访问 Iceberg 数据进行查询加速。
数据集成读取 Iceberg 数据并写入到 Doris 内表。或通过 Doris 计算引擎进行 ZeroETL 操作。
数据写回将任意 Doris 支持读取的数据源数据进行加工后,写回到 Iceberg 表存储。

配置 Catalog​

语法​

CREATE CATALOG [IF NOT EXISTS] catalog_name PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = '<iceberg_catalog_type>',
'warehouse' = '<warehouse>', --optional
{MetaStoreProperties},
{StorageProperties},
{IcebergProperties},
{CommonProperties}
);
  • <iceberg_catalog_type>

    Iceberg Catalog 的类型,支持以下几种:

    • hms:使用 Hive Metastore 作为元数据服务。

    • rest:兼容 Iceberg Rest Catalog 接口的元数据服务。

    • hadoop:直接访问文件系统上存储的元数据。

    • glue:使用 AWS Glue 作为元数据服务。

    • dlf:使用阿里云 DLF 作为元数据服务。

    • s3tables:使用 AWS S3 Tables Catalog 访问 S3 Table Bucket。

  • <warehouse>

    Iceberg 的仓库路径。当 <iceberg_catalog_type> 为 hadoop 时,需指定该参数。

    warehouse 的路径必须指向 Database 路径的上一级。如您的表路径是 s3://bucket/path/to/db1/table1,那么 warehouse 应该是 s3://bucket/path/to/。

  • {MetaStoreProperties}

    MetaStoreProperties 部分用于填写 Metastore 元数据服务连接和认证信息。具体可参阅【支持的元数据服务】部分。

  • {StorageProperties}

    StorageProperties 部分用于填写存储系统相关的连接和认证信息。具体可参阅【支持的存储系统】部分。

  • {IcebergProperties}

    IcebergProperties 部分用于填写一些 Iceberg Catalog 特有的参数。

    • list-all-tables

      自 3.1.2 版本支持。

      针对以 Hive Metastore 作为元数据服务的 Iceberg Catalog。默认为 true。在默认情况下,SHOW TABLES 操作会罗列出当前 Database 下的所有类型的 Table(Hive Metastore 中可能存储了非 Iceberg 类型的表)。

      这种方式性能最好。如果设置为 false,则 Doris 会逐一检查每个 Table 的类型,并只返回 Iceberg 类型的 Table。该模式在表很多的情况下,性能会比较差。

  • {CommonProperties}

    CommonProperties 部分用于填写通用属性。请参阅数据目录概述中【通用属性】部分。

元数据缓存​

为了提升访问外部数据源的性能,Apache Doris 会对 Iceberg 的元数据进行缓存。元数据包括表结构(Schema)、表对象、View 对象和 Manifest 详情等。

提示

对于 Doris 4.1.x 之前的版本,元数据缓存主要由 FE 配置项全局控制,详情请参阅元数据缓存。 从 Doris 4.1.x 开始,Iceberg Catalog 的外表元数据缓存使用统一键 meta.cache.* 进行配置。

缓存属性配置(4.1.x+)​

各引擎 cache entry 使用统一的配置键格式:meta.cache.<engine>.<entry>.{enable,ttl-second,capacity}。

属性示例含义
enabletrue/false是否启用该缓存模块。
ttl-second600、0、-10 表示关闭缓存(即刻生效,可用于查看最新数据);-1 表示永不过期;其他正整数表示按访问时间计算 TTL(秒)。
capacity10000最大缓存条目数(按条目数量计)。0 表示关闭。

生效逻辑说明: 只有当 enable=true 且 ttl-second != 0 且 capacity > 0 时,该模块缓存才会生效。

缓存模块​

Iceberg Catalog 包含以下缓存模块:

模块 (<entry>)属性键前缀缓存内容与影响
schemameta.cache.iceberg.schema.缓存表结构。影响:列新增、删除、类型变更在 Doris 中的可见性。若关闭,每次查询都会拉取最新 Schema。
tablemeta.cache.iceberg.table.缓存 Iceberg 表元数据对象。影响:最新 Snapshot、Partition Spec、Sort Order、表属性等表级元数据在 Doris 中的可见性;若关闭,每次规划都会重新加载表元数据。
viewmeta.cache.iceberg.view.缓存 Iceberg View 元数据对象。影响:View 定义、Schema、属性变更在 Doris 中的可见性。
manifestmeta.cache.iceberg.manifest.缓存 Manifest 详情。主要影响查询规划时重复读取 Manifest 文件的开销,通常不直接决定表或 Snapshot 是否可见。注意:该模块默认关闭,需手动启用。

旧参数映射与转换​

在 4.1.x 之前,Iceberg 表级元数据主要受 FE 全局缓存策略控制,schema.cache.ttl-second 是常见的 Catalog 兼容属性;详见元数据缓存。升级到 4.1.x 后,建议改写为 meta.cache.iceberg.*,并按需要分别配置表、View 和 Manifest 缓存。

4.1 前属性键/旧模型适用范围4.1.x+ 统一键升级建议与影响
schema.cache.ttl-second4.1 前 Iceberg Catalog 兼容属性meta.cache.iceberg.schema.ttl-second控制 Schema 新鲜度。若希望列变更每次查询立即可见,设置为 0。
Iceberg 表信息旧模型4.1 前 FE 全局缓存策略(见旧版元数据缓存文档)meta.cache.iceberg.table.ttl-second控制表级元数据新鲜度。若希望每次查询都读取最新 Snapshot/表属性,设置为 0。
无一一对应的旧 Catalog 键4.1 前未单独暴露 View / Manifest TTLmeta.cache.iceberg.view.*、meta.cache.iceberg.manifest.*这是 4.1.x 中拆分出的新模块。升级后如需保证最新 View 定义,单独调低 view.ttl-second;manifest 主要用于性能优化。

4.1.x 的统一模型把缓存拆分为 enable、ttl-second、capacity 三个维度;旧模型主要描述 TTL/全局缓存行为,不覆盖这些新模块的独立开关和容量上限。升级时建议一并评估是否需要补充 enable/capacity。

最佳实践​

  • 实时查看最新数据:如果您希望每次查询都能看到 Iceberg 表的最新快照或 Schema 变动,可以将 schema 或 table 的 ttl-second 设置为 0。
    -- 关闭表对象缓存,以便感知快照变动
    ALTER CATALOG iceberg_ctl SET PROPERTIES ("meta.cache.iceberg.table.ttl-second" = "0");
  • 性能优化:
    • 启用 Manifest 缓存可以显著提升大表的查询规划速度:
      ALTER CATALOG iceberg_ctl SET PROPERTIES (
      "meta.cache.iceberg.manifest.enable" = "true",
      "meta.cache.iceberg.manifest.ttl-second" = "600"
      );
    • ALTER CATALOG ... SET PROPERTIES 的修改在 Iceberg Catalog 中支持热生效。

可观测性​

可以通过 information_schema.catalog_meta_cache_statistics 系统表观测缓存指标:

SELECT catalog_name, engine_name, entry_name,
effective_enabled, ttl_second, capacity,
estimated_size, hit_rate, load_failure_count, last_error
FROM information_schema.catalog_meta_cache_statistics
WHERE catalog_name = 'iceberg_ctl' AND engine_name = 'iceberg'
ORDER BY entry_name;

该系统表文档见:catalog_meta_cache_statistics。

支持的 Iceberg 版本​

Doris 版本Iceberg SDK 版本
2.11.6.1
3.01.6.1
3.11.9.1
4.01.9.1

支持的 Iceberg 格式​

  • 支持 Iceberg V1/V2 格式。

  • 支持 Position Delete 和 Equality Delete。

  • 支持读取 Deletion Vector(自 4.1.0 版本支持)。

支持的元数据服务​

注意:不同 Doris 版本所支持的服务类型和参数略有区别,请参阅【基础示例】章节。

Iceberg Catalog 功能支持矩阵​

元数据服务表查询视图查询DDL 操作数据写回系统表 (3.1+ 支持)
Hive MetaStore✅✅✅✅✅
AWS Glue✅✅❌❌✅
Aliyun DLF✅✅❌❌❌
Iceberg Rest Catalog✅✅取决于服务提供方取决于服务提供方取决于服务提供方
FileSystem✅✅✅✅✅

支持的存储系统​

注意:不同 Doris 版本所支持的服务类型和参数略有区别,请参阅【基础示例】章节。

支持的数据格式​

列类型映射​

Iceberg TypeDoris TypeComment
booleanboolean
integerint
longbigint
floatfloat
doubledouble
decimal(P, S)decimal(P, S)
datedate
timestamp (Timestamp without timezone)datetime(6)固定映射到精度为 6 的 datetime
timestamptz (Timestamp with timezone)datetime(6)/timestamptz(6)固定映射到精度为 6 的 datetime。由 properties 中 enable.mapping.timestamp_tz(4.0.3 后开始支持)属性控制,默认为 false,则映射到 datetime;为 true 时,则映射到 timestamptz 类型
fixed(N)char(N)
stringstring
binarystring/varbinary由 properties 中 enable.mapping.varbinary(4.0.2 后开始支持)属性控制。默认为 false,则映射到 string;为 true 时,则映射到 varbinary 类型
uuidstring/varbinaryversion < 4.0.2 映射成 string 类型;version == 4.0.2 则映射成 varbinary 类型。version > 4.0.2,由 properties 中 enable.mapping.varbinary 属性控制。默认为 false,则映射到 string
structstruct(2.1.3 版本开始支持)
mapmap(2.1.3 版本开始支持)
listarray
otherUNSUPPORTED

注:

Doris 当前不支持带时区的 Timestamp 类型。所有 timestamp 和 timestamptz 会统一映射到 datetime(N) 类型上。但在读取和写入时,Doris 会根据实际源类型正确处理时区。如通过 SET time_zone=<tz> 指定时区后,会影响 timestamptz 列的读取和写入结果。

可以在 DESCRIBE table_name 语句中的 Extra 列查看源类型是否带时区信息。如显示 WITH_TIMEZONE,则表示源类型是带时区的类型(该功能自 3.1.0 版本支持)。

4.0.3 后开始支持,可以映射 timestamptz (Timestamp with timezone) 到 Doris 的 timestamptz 类型。

Namespace 映射​

Iceberg 的元数据层级关系是 Catalog -> Namespace -> Table。其中 Namespace 可以有多级(Nested Namespace)。

      ┌─────────┐             
│ Catalog │
└────┬────┘
│
┌─────┴─────┐
┌──▼──┐ ┌──▼──┐
│ NS1 │ │ NS2 │
└──┬──┘ └──┬──┘
│ │
┌────▼───┐ ┌──▼──┐
│ Table1 │ │ NS3 │
└────────┘ └──┬──┘
│
┌──────┴───────┐
┌────▼───┐ ┌────▼───┐
│ Table2 │ │ Table3 │
└────────┘ └────────┘

自 3.1.2 版本开始,对于 Iceberg Rest Catalog,Doris 支持对 Nested Namespace 的映射。

在上述示例中,会按照如下逻辑映射为 Doris 的元数据:

CatalogDatabaseTable
CatalogNS1Table1
CatalogNS2.NS3Table2
CatalogNS1.NS3Table3

对 Nested Namespace 的支持需要显式开启,具体请参阅 Iceberg Rest Catalog

基础示例​

Hive Metastore​

3.1+ 版本

访问未开启 Kerberos 认证的 HMS 和 HDFS 服务

CREATE CATALOG iceberg_hms_on_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hms',
'warehouse' = 'hdfs://127.0.0.1:8320/user/iceberg/warehouse/iceberg-hms-hdfs-warehouse',
'hive.metastore.uris' = 'thrift://127.0.0.1:9383',
'fs.defaultFS' = 'hdfs://127.0.0.1:8320',
'hadoop.username' = 'doris'
);

访问开启 Kerberos 认证的 HMS 和 HDFS 服务

CREATE CATALOG iceberg_hms_on_hdfs_kerberos_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hms',
'warehouse' = 'hdfs://127.0.0.1:8520/iceberg-hms-hdfs-warehouse',
'hive.metastore.uris' = 'thrift://127.0.0.1:9583',
'hive.metastore.client.principal' = 'hive/presto-master.docker.cluster@LABS.TERADATA.COM',
'hive.metastore.client.keytab' = '/keytabs/hive-presto-master.keytab',
'hive.metastore.service.principal' = 'hive/hadoop-master@LABS.TERADATA.COM',
'hive.metastore.sasl.enabled' = 'true',
'hive.metastore.authentication.type' = 'kerberos',
'hadoop.security.auth_to_local' = 'RULE:[2:\$1@\$0](.*@LABS.TERADATA.COM)s/@.*//
RULE:[2:\$1@\$0](.*@OTHERLABS.TERADATA.COM)s/@.*//
RULE:[2:\$1@\$0](.*@OTHERREALM.COM)s/@.*//
DEFAULT',
'fs.defaultFS' = 'hdfs://127.0.0.1:8520',
'hadoop.security.authentication' = 'kerberos',
'hadoop.kerberos.principal' = 'hive/presto-master.docker.cluster@LABS.TERADATA.COM',
'hadoop.kerberos.keytab' = '/keytabs/hive-presto-master.keytab'
);
2.1 & 3.0 版本

访问未开启 Kerberos 认证的 HMS 和 HDFS 服务

CREATE CATALOG iceberg_hms_on_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hms',
'hive.metastore.uris' = 'thrift://127.0.0.1:9383',
'warehouse' = 'hdfs://127.0.0.1:8320/user/iceberg/warehouse/iceberg-hms-hdfs-warehouse',
'hadoop.username' = 'doris',
'fs.defaultFS' = 'hdfs://127.0.0.1:8320'
);

访问开启 Kerberos 认证的 HMS 和 HDFS 服务

CREATE CATALOG iceberg_hms_on_hdfs_kerberos_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hms',
'warehouse' = 'hdfs://127.0.0.1:8520/iceberg-hms-hdfs-warehouse',
'hive.metastore.uris' = 'thrift://127.0.0.1:9583',
'hive.metastore.kerberos.principal' = 'hive/hadoop-master@LABS.TERADATA.COM',
'hive.metastore.sasl.enabled' = 'true',
'hive.metastore.authentication.type' = 'kerberos',
'hadoop.security.auth_to_local' = 'RULE:[2:\$1@\$0](.*@LABS.TERADATA.COM)s/@.*//
RULE:[2:\$1@\$0](.*@OTHERLABS.TERADATA.COM)s/@.*//
RULE:[2:\$1@\$0](.*@OTHERREALM.COM)s/@.*//
DEFAULT',
'fs.defaultFS' = 'hdfs://127.0.0.1:8520',
'hadoop.security.authentication' = 'kerberos',
'hadoop.kerberos.principal' = 'hive/presto-master.docker.cluster@LABS.TERADATA.COM',
'hadoop.kerberos.keytab' = '/keytabs/hive-presto-master.keytab'
);

AWS Glue​

3.1+ 版本

AWS Glue 和 S3 存储服务共用一套认证信息。

CREATE CATALOG `iceberg_glue_on_s3_catalog_` PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'glue',
'warehouse' = 's3://bucket/iceberg-glue-s3-warehouse',
'glue.region' = 'ap-northeast-1',
'glue.endpoint' = 'https://glue.ap-northeast-1.amazonaws.com',
'glue.access_key' = '<ak>',
'glue.secret_key' = '<sk>'
);

Glue 服务的认证信息和 S3 的认证信息不一致时,可以通过以下方式单独指定 S3 的认证信息。

CREATE CATALOG `iceberg_glue_on_s3_catalog_` PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'glue',
'warehouse' = 's3://selectdb-qa-datalake-test/iceberg-glue-s3-warehouse',
'glue.region' = 'ap-northeast-1',
'glue.endpoint' = 'https://glue.ap-northeast-1.amazonaws.com',
'glue.access_key' = '<ak>',
'glue.secret_key' = '<sk>',
's3.endpoint' = 's3.ap-northeast-1.amazonaws.com',
's3.region' = 'ap-northeast-1',
's3.access_key' = '<ak>',
's3.secret_key' = '<sk>'
);

使用 IAM Assumed Role 的方式获取 S3 访问凭证 (3.1.2+ 支持)

CREATE CATALOG `glue_iceberg_iamrole` PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'glue',
'warehouse' = 's3://bucket/warehouse',
'glue.region' = 'us-east-1',
'glue.endpoint' = 'https://glue.us-east-1.amazonaws.com',
'glue.role_arn' = '<role_arn>'
);
2.1 & 3.0 版本

AWS Glue 和 S3 存储服务共用一套认证信息。

非 EC2 环境下,需要使用 aws configure 配置 Credentials 信息,同时在 ~/.aws 目录下生成 credentials 文件。

CREATE CATALOG glue PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'glue',
'glue.endpoint' = 'https://glue.us-east-1.amazonaws.com',
'glue.access_key' = '<ak>',
'glue.secret_key' = '<sk>'
);

Aliyun DLF​

4.1+ 版本
CREATE CATALOG iceberg_dlf2_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'rest',
'iceberg.rest.uri' = 'http://<region>-vpc.dlf.aliyuncs.com/iceberg',
'warehouse' = '<your-catalog-name>',
'iceberg.rest.sigv4-enabled' = 'true',
'iceberg.rest.signing-name' = 'DlfNext',
'iceberg.rest.access-key-id' = '<ak>',
'iceberg.rest.secret-access-key' = '<sk>',
'iceberg.rest.signing-region' = '<region>',
'iceberg.rest.vended-credentials-enabled' = 'true',
'io-impl' = 'org.apache.iceberg.rest.DlfFileIO',
'fs.oss.support' = 'true'
);
3.1+ 版本
CREATE CATALOG iceberg_dlf_catalog_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'dlf',
'warehouse' = 'oss://bucket/iceberg-dlf-oss-warehouse',
'dlf.uid' = '203225413946383283',
'dlf.catalog_id' = 'p2_regression_case',
'dlf.region' = 'cn-beijing',
'dlf.endpoint' = 'datalake.cn-beijing.aliyuncs.com',
'dlf.access_key' = '<ak>',
'dlf.secret_key' = '<sk>'
);
2.1 & 3.0 版本
CREATE CATALOG iceberg_dlf_catalog_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'dlf',
'warehouse' = 'oss://bucket/iceberg-dlf-oss-warehouse',
'dlf.uid' = '203225413946383283',
'dlf.catalog.id' = 'catalog_id',
'dlf.region' = 'cn-beijing',
'dlf.access_key' = '<ak>',
'dlf.secret_key' = '<sk>'
);

Iceberg Rest Catalog​

3.1+ 版本
CREATE CATALOG iceberg_static_s3 PROPERTIES (
'type' = 'iceberg',
'warehouse' = 's3://warehouse',
'iceberg.catalog.type' = 'rest',
'iceberg.rest.uri' = 'http://127.0.0.1:8181',
's3.endpoint' = 'https://s3.ap-east-1.amazonaws.com',
's3.access_key' = '<ak>',
's3.secret_key' = '<sk>',
's3.region' = 'ap-east-1'
);
2.1 & 3.0 版本
CREATE CATALOG iceberg_static_s3 PROPERTIES (
'type' = 'iceberg',
'warehouse' = 's3://warehouse',
'iceberg.catalog.type' = 'rest',
'uri' = 'http://127.0.0.1:8181',
's3.endpoint' = 'https://s3.ap-east-1.amazonaws.com',
's3.access_key' = '<ak>',
's3.secret_key' = '<sk>',
's3.region' = 'ap-east-1'
);

Iceberg JDBC Catalog​

提示

该功能为试验功能,自 4.1.0 版本支持。

4.1+ 版本

使用 PostgreSQL 作为元数据存储,配合 S3 存储

CREATE CATALOG iceberg_jdbc_postgresql PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'jdbc',
'iceberg.jdbc.uri' = 'jdbc:postgresql://127.0.0.1:5432/iceberg_db',
'iceberg.jdbc.user' = 'iceberg_user',
'iceberg.jdbc.password' = 'password',
'iceberg.jdbc.init-catalog-tables' = 'true',
'iceberg.jdbc.schema-version' = 'V1',
'iceberg.jdbc.driver_class' = 'org.postgresql.Driver',
'iceberg.jdbc.driver_url' = '<jdbc_driver_jar>',
'warehouse' = 's3://bucket/warehouse',
's3.access_key' = '<ak>',
's3.secret_key' = '<sk>',
's3.endpoint' = 'https://s3.us-east-1.amazonaws.com',
's3.region' = 'us-east-1'
);

FileSystem​

3.1+ 版本

访问未开启 Kerberos 认证的 HDFS 服务

CREATE CATALOG iceberg_fs_on_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hadoop',
'warehouse' = 'hdfs://127.0.0.1:8320/user/iceberg/warehouse/iceberg-fs-hdfs-warehouse',
'hadoop.username' = 'doris',
'fs.defaultFS' = 'hdfs://127.0.0.1:8320'
);

访问开启了 Kerberos 认证的 HDFS 服务

CREATE CATALOG iceberg_fs_on_hdfs_kerberos_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hadoop',
'warehouse' = 'hdfs://127.0.0.1:8520/iceberg-fs-hdfs-warehouse',
'fs.defaultFS' = 'hdfs://127.0.0.1:8520',
'hadoop.security.authentication' = 'kerberos',
'hadoop.kerberos.principal' = 'hive/presto-master.docker.cluster@LABS.TERADATA.COM',
'hadoop.kerberos.keytab' = '/keytabs/hive-presto-master.keytab'
);
2.1 & 3.0 版本

访问未开启 Kerberos 认证的 HDFS 服务

CREATE CATALOG iceberg_fs_on_hdfs_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hadoop',
'warehouse' = 'hdfs://127.0.0.1:8320/user/iceberg/warehouse/iceberg-fs-hdfs-warehouse',
'hadoop.username' = 'doris',
'fs.defaultFS' = 'hdfs://127.0.0.1:8320'
);

访问开启了 Kerberos 认证的 HDFS 服务

CREATE CATALOG iceberg_fs_on_hdfs_kerberos_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'hadoop',
'warehouse' = 'hdfs://127.0.0.1:8520/iceberg-fs-hdfs-warehouse',
'fs.defaultFS' = 'hdfs://127.0.0.1:8520',
'hadoop.security.authentication' = 'kerberos',
'hadoop.kerberos.principal' = 'hive/presto-master.docker.cluster@LABS.TERADATA.COM',
'hadoop.kerberos.keytab' = '/keytabs/hive-presto-master.keytab'
);

AWS S3 Tables​

3.1+ 版本

可参阅 集成 S3 Tables 文档。

CREATE CATALOG aws_s3_tables PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'rest',
'warehouse' = 'arn:aws:s3tables:us-east-1:<account_id>:bucket/<s3_table_bucket_name>',
'iceberg.rest.uri' = 'https://s3tables.us-east-1.amazonaws.com/iceberg',
'iceberg.rest.sigv4-enabled' = 'true',
'iceberg.rest.signing-name' = 's3tables',
'iceberg.rest.signing-region' = 'us-east-1',
'iceberg.rest.access-key-id' = '<ak>',
'iceberg.rest.secret-access-key' = '<sk>'
);
3.0.6+ 版本

可参阅 集成 S3 Tables 文档。

CREATE CATALOG test_s3tables_catalog PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 's3tables',
'warehouse' = 'arn:aws:s3tables:ap-east-1:9527:bucket/doris-s3-table-bucket',
's3.region' = 'ap-east-1',
's3.endpoint' = 'https://s3.ap-east-1.amazonaws.com',
's3.access_key' = '<ak>',
's3.secret_key' = '<sk>'
);

查询操作​

基础查询​

-- 1. switch to catalog, use database and query
SWITCH iceberg;
USE iceberg_db;
SELECT * FROM iceberg_tbl LIMIT 10;

-- 2. use iceberg database directly
USE iceberg.iceberg_db;
SELECT * FROM iceberg_tbl LIMIT 10;

-- 3. use full qualified name to query
SELECT * FROM iceberg.iceberg_db.iceberg_tbl LIMIT 10;

时间旅行​

支持读取 Iceberg 表指定的 Snapshot。

默认情况下,读取请求只会读取最新版本的快照。

可以通过 iceberg_meta() 表函数查询指定 Iceberg 表的 Snapshot:

SELECT * FROM iceberg_meta(
'table' = 'iceberg_ctl.iceberg_db.iceberg_tbl',
'query_type' = 'snapshots'
)\G

*************************** 1. row ***************************
committed_at: 2024-11-28 11:07:29
snapshot_id: 8903826400153112036
parent_id: -1
operation: append
manifest_list: oss://path/to/metadata/snap-8903826400153112036-1-3835e66d-9a18-4cb0-b9b0-9ec80527ad8d.avro
summary: {"added-data-files":"2","added-records":"3","added-files-size":"2742","changed-partition-count":"2","total-records":"3","total-files-size":"2742","total-data-files":"2","total-delete-files":"0","total-position-deletes":"0","total-equality-deletes":"0"}
*************************** 2. row ***************************
committed_at: 2024-11-28 11:10:11
snapshot_id: 6099853805930794326
parent_id: 8903826400153112036
operation: append
manifest_list: oss://path/to/metadata/snap-6099853805930794326-1-dd46a1bd-219b-4fb0-bb46-ac441d8b3105.avro
summary: {"added-data-files":"1","added-records":"1","added-files-size":"1367","changed-partition-count":"1","total-records":"4","total-files-size":"4109","total-data-files":"3","total-delete-files":"0","total-position-deletes":"0","total-equality-deletes":"0"}

可以使用 FOR TIME AS OF 和 FOR VERSION AS OF 语句,根据快照 ID 或者快照产生的时间读取历史版本的数据。示例如下:

SELECT * FROM iceberg_tbl FOR TIME AS OF "2022-10-07 17:20:37";

SELECT * FROM iceberg_tbl FOR VERSION AS OF 868895038966572;

Branch 和 Tag​

该功能自 3.1.0 版本支持。

关于 Branch、Tag 的创建、删除和维护操作,请参阅【管理 Branch & Tag】章节。

支持读取指定 Iceberg 表的分支(Branch)和标签(Tag)。

支持多种不同的语法形式,以兼容 Spark/Trino 等系统的语法。

-- BRANCH
SELECT * FROM iceberg_tbl@branch(branch1);
SELECT * FROM iceberg_tbl@branch("name" = "branch1");
SELECT * FROM iceberg_tbl FOR VERSION AS OF 'branch1';

-- TAG
SELECT * FROM iceberg_tbl@tag(tag1);
SELECT * FROM iceberg_tbl@tag("name" = "tag1");
SELECT * FROM iceberg_tbl FOR VERSION AS OF 'tag1';

对于 FOR VERSION AS OF 语法,Doris 会根据后面的参数,自动判断是时间戳还是 Branch/Tag 名称。

视图查询​

该功能自 3.1.0 版本支持

支持查询 Iceberg 视图。视图查询方式与普通表方式一样。有以下几点注意事项:

  • 仅支持 hms 类型的 Iceberg Catalog。
  • 视图的定义 SQL 需要与 Doris SQL 方言兼容,否则会出现解析错误(后续版本会提供方言转换功能)。

Iceberg V3 隐藏列(Row Lineage)​

提示

该功能为实验功能,自 4.1.0 版本支持。

Doris 支持查询 Iceberg V3 表的行血缘(Row Lineage)隐藏列。Iceberg V3 引入了包含行血缘数据的系统隐藏列,用于追踪数据的变更记录,非常适合用于增量同步或数据审查:

  • _row_id:唯一标识数据行的数值(基于数据写入时所在文件和位置生成)。
  • _last_updated_sequence_number:最后一次修改此行的 Sequence Number。当执行 UPDATE 或 MERGE INTO 操作更新数据时,该数值会随着版本号自动递增。

如何查询隐藏列:

默认情况下,使用 DESC 或 SELECT * 时隐藏列不可见。你可以通过直接指定列名来显式查询它们:

SELECT id, _row_id, _last_updated_sequence_number FROM iceberg_tbl;

你也可以通过开启会话变量 show_hidden_columns 来使隐藏列在常规展示(如 DESC 和 SELECT * 中)可见:

SET show_hidden_columns = true;
DESC iceberg_tbl;

注意事项:

  1. 必须确保 Iceberg 表的格式版本(format-version)配置为 3 时,才会生成上述行级别参数。对于 V1 和 V2 格式表查询行血缘列可能报错。
  2. 隐藏列由系统进行维护,不允许用户在使用 INSERT 等写入语句时显式指定写入 _row_id 或 _last_updated_sequence_number 列。

系统表​

该功能自 3.1.0 版本支持

Doris 支持查询 Iceberg 系统表,用于查询表的相关元信息。支持使用系统表查看快照历史、清单文件、数据文件、分区等信息。

要访问 Iceberg 表的元数据,可以在表名后添加 $ 符号,后跟系统表名称:

SELECT * FROM iceberg_table$system_table_name;

例如,要查看表的历史记录,可以执行:

SELECT * FROM iceberg_table$history;

all_manifests 系统表从 4.0.4 版本开始支持。

position_deletes 系统表尚未支持,计划在以后版本中支持。

entries​

显示表当前快照的所有清单条目:

all_entries 和 entries 类似,区别在于 all_entries 包含了所有快照的条目,而 entries 只包含当前快照的条目。

SELECT * FROM iceberg_table$entries;

结果:

+--------+---------------------+-----------------+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| status | snapshot_id | sequence_number | file_sequence_number | data_file | readable_metrics |
+--------+---------------------+-----------------+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 2 | 4890031351138056789 | 1 | 1 | {"content":0, "file_path":"s3://.../iceberg_table/data/id=1/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00001.parquet", "file_format":"PARQUET", "spec_id":0, "partition":{"id":1}, "record_count":1, "file_size_in_bytes":625, "column_sizes":{1:36, 2:41}, "value_counts":{1:1, 2:1}, "null_value_counts":{1:0, 2:0}, "nan_value_counts":{}, "lower_bounds":{1:" ", 2:"Alice"}, "upper_bounds":{1:" ", 2:"Alice"}, "key_metadata":null, "split_offsets":[4], "equality_ids":null, "sort_order_id":0, "first_row_id":null, "referenced_data_file":null, "content_offset":null, "content_size_in_bytes":null} | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":1, "upper_bound":1}, "name":{"column_size":41, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Alice", "upper_bound":"Alice"}} |
| 0 | 1851184769713369003 | 1 | 1 | {"content":0, "file_path":"s3://.../iceberg_table/data/id=2/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00002.parquet", "file_format":"PARQUET", "spec_id":0, "partition":{"id":2}, "record_count":1, "file_size_in_bytes":611, "column_sizes":{1:36, 2:39}, "value_counts":{1:1, 2:1}, "null_value_counts":{1:0, 2:0}, "nan_value_counts":{}, "lower_bounds":{1:" ", 2:"Bob"}, "upper_bounds":{1:" ", 2:"Bob"}, "key_metadata":null, "split_offsets":[4], "equality_ids":null, "sort_order_id":0, "first_row_id":null, "referenced_data_file":null, "content_offset":null, "content_size_in_bytes":null} | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":2, "upper_bound":2}, "name":{"column_size":39, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Bob", "upper_bound":"Bob"}} |
+--------+---------------------+-----------------+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

files​

显示当前快照的文件信息:

SELECT * FROM iceberg_table$files;

结果:

+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+-----------+--------------+--------------------+--------------+--------------+-------------------+------------------+-----------------------+-----------------------+--------------+---------------+--------------+---------------+--------------+----------------------+----------------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| content | file_path | file_format | spec_id | partition | record_count | file_size_in_bytes | column_sizes | value_counts | null_value_counts | nan_value_counts | lower_bounds | upper_bounds | key_metadata | split_offsets | equality_ids | sort_order_id | first_row_id | referenced_data_file | content_offset | content_size_in_bytes | readable_metrics |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+-----------+--------------+--------------------+--------------+--------------+-------------------+------------------+-----------------------+-----------------------+--------------+---------------+--------------+---------------+--------------+----------------------+----------------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 0 | s3://.../iceberg_table/data/id=2/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00002.parquet | PARQUET | 0 | {"id":2} | 1 | 611 | {1:36, 2:39} | {1:1, 2:1} | {1:0, 2:0} | {} | {1:" ", 2:"Bob"} | {1:" ", 2:"Bob"} | NULL | [4] | NULL | 0 | NULL | NULL | NULL | NULL | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":2, "upper_bound":2}, "name":{"column_size":39, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Bob", "upper_bound":"Bob"}} |
| 0 | s3://.../iceberg_table/data/id=4/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00004.parquet | PARQUET | 0 | {"id":4} | 1 | 618 | {1:36, 2:40} | {1:1, 2:1} | {1:0, 2:0} | {} | {1:" ", 2:"Dave"} | {1:" ", 2:"Dave"} | NULL | [4] | NULL | 0 | NULL | NULL | NULL | NULL | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":4, "upper_bound":4}, "name":{"column_size":40, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Dave", "upper_bound":"Dave"}} |
| 0 | s3://.../iceberg_table/data/id=6/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00006.parquet | PARQUET | 0 | {"id":6} | 1 | 625 | {1:36, 2:41} | {1:1, 2:1} | {1:0, 2:0} | {} | {1:" ", 2:"Frank"} | {1:" ", 2:"Frank"} | NULL | [4] | NULL | 0 | NULL | NULL | NULL | NULL | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":6, "upper_bound":6}, "name":{"column_size":41, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Frank", "upper_bound":"Frank"}} |
| 0 | s3://.../iceberg_table/data/id=8/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00008.parquet | PARQUET | 0 | {"id":8} | 1 | 625 | {1:36, 2:41} | {1:1, 2:1} | {1:0, 2:0} | {} | {1:" ", 2:"Heidi"} | {1:" ", 2:"Heidi"} | NULL | [4] | NULL | 0 | NULL | NULL | NULL | NULL | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":8, "upper_bound":8}, "name":{"column_size":41, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Heidi", "upper_bound":"Heidi"}} |
| 0 | s3://.../iceberg_table/data/id=10/00000-16-79ef2fd7-9997-47eb-a91a-9f7af8201315-0-00010.parquet | PARQUET | 0 | {"id":10} | 1 | 618 | {1:36, 2:40} | {1:1, 2:1} | {1:0, 2:0} | {} | {1:" ", 2:"Judy"} | {1:" ", 2:"Judy"} | NULL | [4] | NULL | 0 | NULL | NULL | NULL | NULL | {"id":{"column_size":36, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":10, "upper_bound":10}, "name":{"column_size":40, "value_count":1, "null_value_count":0, "nan_value_count":null, "lower_bound":"Judy", "upper_bound":"Judy"}} |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+-----------+--------------+--------------------+--------------+--------------+-------------------+------------------+-----------------------+-----------------------+--------------+---------------+--------------+---------------+--------------+----------------------+----------------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

类似于 files 系统表,还可以通过以下系统表查询特定类型的文件信息:

-- 查询当前快照的数据文件
SELECT * FROM iceberg_table$data_files;

-- 查询当前快照的删除文件
SELECT * FROM iceberg_table$delete_files;

-- 查询所有快照的所有文件(包括数据和删除文件)
SELECT * FROM iceberg_table$all_files;

-- 查询所有快照的数据文件
SELECT * FROM iceberg_table$all_data_files;

-- 查询所有快照的删除文件
SELECT * FROM iceberg_table$all_delete_files;

这些表的结果格式与 files 系统表相似,但分别专注于数据文件或删除文件。all_ 前缀的系统表包含了所有快照中的文件,而不仅仅是当前快照的文件。

注意:当表中不存在特定类型的文件时(例如,表中没有删除文件时查询 delete_files),查询结果可能为空。

history​

显示表的所有历史记录:

SELECT * FROM iceberg_table$history;

结果:

+----------------------------+---------------------+---------------------+---------------------+
| made_current_at | snapshot_id | parent_id | is_current_ancestor |
+----------------------------+---------------------+---------------------+---------------------+
| 2025-06-12 22:29:16.357000 | 1851184769713369003 | NULL | 1 |
| 2025-06-12 22:29:39.922000 | 4890031351138056789 | 1851184769713369003 | 1 |
+----------------------------+---------------------+---------------------+---------------------+

manifests​

显示表的当前 manifest 文件信息:

SELECT * FROM iceberg_table$manifests;

结果:

+---------+------------------------------------------------------------------------------------------------------------------------------------------------+--------+-------------------+---------------------+------------------------+---------------------------+--------------------------+--------------------------+-----------------------------+----------------------------+--------------------------------------------------------------------------------+
| content | path | length | partition_spec_id | added_snapshot_id | added_data_files_count | existing_data_files_count | deleted_data_files_count | added_delete_files_count | existing_delete_files_count | deleted_delete_files_count | partition_summaries |
+---------+------------------------------------------------------------------------------------------------------------------------------------------------+--------+-------------------+---------------------+------------------------+---------------------------+--------------------------+--------------------------+-----------------------------+----------------------------+--------------------------------------------------------------------------------+
| 0 | s3://.../iceberg_table/metadata/3194eb8b-5ea4-4cbe-95ba-073229458e7b-m0.avro | 7138 | 0 | 4890031351138056789 | 0 | 5 | 5 | 0 | 0 | 0 | [{"contains_null":0, "contains_nan":0, "lower_bound":"1", "upper_bound":"10"}] |
+---------+------------------------------------------------------------------------------------------------------------------------------------------------+--------+-------------------+---------------------+------------------------+---------------------------+--------------------------+--------------------------+-----------------------------+----------------------------+--------------------------------------------------------------------------------+

all_manifests​

该功能自 4.0.4 版本支持

显示表的所有有效快照的 manifest 文件信息:

all_manifests 和 manifests 的结构相同,区别在于 all_manifests 包含了所有有效快照的 manifest 文件,而 manifests 只包含当前快照的 manifest 文件。

SELECT * FROM iceberg_table$all_manifests;

结果格式与 manifests 系统表相同。

metadata_log_entries​

显示表的元数据日志条目:

SELECT * FROM iceberg_table$metadata_log_entries;

结果:

+----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+------------------+------------------------+
| timestamp | file | latest_snapshot_id | latest_schema_id | latest_sequence_number |
+----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+------------------+------------------------+
| 2025-06-12 22:29:06.948000 | s3://.../iceberg_table/metadata/00000-e373aa16-15f1-4e69-ae7d-5ed64199cf9a.metadata.json | NULL | NULL | NULL |
| 2025-06-12 22:29:16.357000 | s3://.../iceberg_table/metadata/00001-bbc8e244-e41c-4958-92f4-63b8c3ee1196.metadata.json | 1851184769713369003 | 0 | 1 |
| 2025-06-12 22:29:39.922000 | s3://.../iceberg_table/metadata/00002-7dc00d6a-6269-4200-9d28-5f8c1c6b9f99.metadata.json | 4890031351138056789 | 0 | 2 |
+----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+------------------+------------------------+

partitions​

显示表的当前分区信息:

SELECT * FROM iceberg_table$partitions;

结果:

+-----------+---------+--------------+------------+-------------------------------+------------------------------+----------------------------+------------------------------+----------------------------+----------------------------+--------------------------+
| partition | spec_id | record_count | file_count | total_data_file_size_in_bytes | position_delete_record_count | position_delete_file_count | equality_delete_record_count | equality_delete_file_count | last_updated_at | last_updated_snapshot_id |
+-----------+---------+--------------+------------+-------------------------------+------------------------------+----------------------------+------------------------------+----------------------------+----------------------------+--------------------------+
| {"id":8} | 0 | 1 | 1 | 625 | 0 | 0 | 0 | 0 | 2025-06-12 22:29:16.357000 | 1851184769713369003 |
| {"id":6} | 0 | 1 | 1 | 625 | 0 | 0 | 0 | 0 | 2025-06-12 22:29:16.357000 | 1851184769713369003 |
| {"id":10} | 0 | 1 | 1 | 618 | 0 | 0 | 0 | 0 | 2025-06-12 22:29:16.357000 | 1851184769713369003 |
| {"id":4} | 0 | 1 | 1 | 618 | 0 | 0 | 0 | 0 | 2025-06-12 22:29:16.357000 | 1851184769713369003 |
| {"id":2} | 0 | 1 | 1 | 611 | 0 | 0 | 0 | 0 | 2025-06-12 22:29:16.357000 | 1851184769713369003 |
+-----------+---------+--------------+------------+-------------------------------+------------------------------+----------------------------+------------------------------+----------------------------+----------------------------+--------------------------+

注意:

  1. 对于非分区表,partitions 表将不包含 partition 和 spec_id 字段。
  2. partitions 表显示当前快照中包含数据文件或删除文件的分区。但是,删除文件未应用,因此在某些情况下,即使分区的所有数据行都已被删除文件标记为已删除,分区仍可能显示。

refs​

显示表的所有已知快照引用(分支和标签):

SELECT * FROM iceberg_table$refs;

结果:

+------+--------+---------------------+-------------------------+-----------------------+------------------------+
| name | type | snapshot_id | max_reference_age_in_ms | min_snapshots_to_keep | max_snapshot_age_in_ms |
+------+--------+---------------------+-------------------------+-----------------------+------------------------+
| main | BRANCH | 4890031351138056789 | NULL | NULL | NULL |
+------+--------+---------------------+-------------------------+-----------------------+------------------------+

snapshots​

显示表的所有有效快照:

SELECT * FROM iceberg_table$snapshots;

结果:

+----------------------------+---------------------+---------------------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| committed_at | snapshot_id | parent_id | operation | manifest_list | summary |
+----------------------------+---------------------+---------------------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 2025-06-12 22:29:16.357000 | 1851184769713369003 | NULL | append | s3://.../iceberg_table/metadata/snap-1851184769713369003-1-82059f57-821a-4983-b083-002cc2cde313.avro | {"spark.app.id":"application_1738810850199_0472", "added-data-files":"10", "added-records":"10", "added-files-size":"6200", "changed-partition-count":"10", "total-records":"10", "total-files-size":"6200", "total-data-files":"10", "total-delete-files":"0", "total-position-deletes":"0", "total-equality-deletes":"0"} |
| 2025-06-12 22:29:39.922000 | 4890031351138056789 | 1851184769713369003 | overwrite | s3://.../iceberg_table/metadata/snap-4890031351138056789-1-3194eb8b-5ea4-4cbe-95ba-073229458e7b.avro | {"spark.app.id":"application_1738810850199_0472", "deleted-data-files":"5", "deleted-records":"5", "removed-files-size":"3103", "changed-partition-count":"5", "total-records":"5", "total-files-size":"3097", "total-data-files":"5", "total-delete-files":"0", "total-position-deletes":"0", "total-equality-deletes":"0"} |
+----------------------------+---------------------+---------------------+-----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

写入操作​

INSERT INTO​

INSERT 操作会将数据以追加的方式写入到目标表中。

例如:

INSERT INTO iceberg_tbl values (val1, val2, val3, val4);
INSERT INTO iceberg.iceberg_db.iceberg_tbl SELECT col1, col2 FROM internal.db1.tbl1;

INSERT INTO iceberg_tbl(col1, col2) values (val1, val2);
INSERT INTO iceberg_tbl(col1, col2, partition_col1, partition_col2) values (1, 2, "beijing", "2023-12-12");

自 3.1.0 版本,支持写入数据到指定分支:

INSERT INTO iceberg_tbl@branch(b1) values (val1, val2, val3, val4);
INSERT INTO iceberg_tbl@branch(b1) (col3, col4) values (val3, val4);

INSERT OVERWRITE​

INSERT OVERWRITE 会使用新的数据完全覆盖原有表中的数据。

INSERT OVERWRITE TABLE iceberg_tbl VALUES(val1, val2, val3, val4)
INSERT OVERWRITE TABLE iceberg.iceberg_db.iceberg_tbl(col1, col2) SELECT col1, col2 FROM internal.db1.tbl1;

自 3.1.0 版本,支持写入数据到指定分支:

INSERT OVERWRITE TABLE iceberg_tbl@branch(b1) values (val1, val2, val3, val4);
INSERT OVERWRITE TABLE iceberg_tbl@branch(b1) (col3, col4) values (val3, val4);

自 4.1.0 版本,支持写入数据到静态分区,或者静态分区和动态分区混合使用:

-- Full Static Partition
INSERT OVERWRITE TABLE iceberg_tbl PARTITION (dt='2025-01-25', region='bj')
SELECT id, name FROM source_table;

-- Hybrid Partition Mode: "dt" is static, "region" comes from SELECT dynamically
INSERT OVERWRITE TABLE iceberg_tbl PARTITION (dt='2025-01-25')
SELECT id, name, region FROM source_table;

CTAS​

可以通过 CTAS 语句创建 Iceberg 表并写入数据:

CREATE TABLE iceberg_ctas AS SELECT * FROM other_table;

CTAS 支持指定文件格式、分区方式等信息。

CREATE TABLE iceberg_ctas
PARTITION BY LIST (pt1, pt2) ()
AS SELECT col1,pt1,pt2 FROM part_ctas_src WHERE col1>0;

CREATE TABLE iceberg.iceberg_db.iceberg_ctas (col1,col2,pt1)
PARTITION BY LIST (pt1) ()
PROPERTIES (
'write-format'='parquet',
'compression-codec'='zstd'
)
AS SELECT col1,pt1 as col2,pt2 as pt1 FROM test_ctas.part_ctas_src WHERE col1>0;

写入数据到 Branch​

该功能自 3.1.0 版本支持

INSERT INTO iceberg_table@branch(b1) SELECT * FROM other_table;
INSERT OVERWRITE TABLE iceberg_table@branch(b1) SELECT * FROM other_table;

DELETE​

提示

该功能为实验功能,自 4.1.0 版本支持。

DELETE 操作用于删除 Iceberg 表中满足指定条件的数据行。主要基于以下底层机制实现:对于 V2 格式表,系统将写入 Position Delete 文件;对于 V3 格式表,系统将写入 Puffin 格式的 Deletion Vectors 文件。

前提条件:

  • Iceberg 表的格式版本(format-version)必须为 2 或更高版本。V1 格式的表不支持 DELETE 操作。

语法:

DELETE FROM iceberg_tbl WHERE <condition>;

示例:

-- 删除单行
DELETE FROM iceberg_tbl WHERE id = 1;

-- 使用复合条件删除
DELETE FROM iceberg_tbl WHERE age > 30 AND name = 'Bob';

-- 使用子查询删除
DELETE FROM iceberg_tbl WHERE id IN (SELECT id FROM other_table WHERE status = 'inactive');

分区表的 DELETE 操作用法一致:

DELETE FROM iceberg_partition_tbl WHERE id = 10;

UPDATE​

提示

该功能为实验功能,自 4.1.0 版本支持。

UPDATE 操作用于修改 Iceberg 表中满足指定条件的数据行。内部实现为先写入删除标记信息(V2 使用 Position Delete 文件,V3 使用 Puffin 格式的 Deletion Vectors 文件)标记旧数据行失效,然后追加写入更新后的新数据文件(如果开启的是 V3 格式选项,新插入行的提取追踪值 _last_updated_sequence_number 将会自动推进)。

前提条件:

  • Iceberg 表的格式版本(format-version)必须为 2 或更高版本。V1 格式的表不支持 UPDATE 操作。

语法:

UPDATE iceberg_tbl SET <column1> = <value1> [, <column2> = <value2>, ...] WHERE <condition>;

示例:

-- 更新单列
UPDATE iceberg_tbl SET name = 'Alice_new' WHERE id = 1;

-- 更新多列
UPDATE iceberg_tbl SET name = 'Updated', age = 28 WHERE id = 2;

-- 使用表达式更新
UPDATE iceberg_tbl SET age = age * 2 + 1, name = concat(name, '-modified') WHERE id = 3;

-- 使用子查询作为条件
UPDATE iceberg_tbl SET name = 'UpdatedViaSubquery' WHERE id IN (SELECT id FROM other_table);

分区表的 UPDATE 操作用法一致:

UPDATE iceberg_partition_tbl SET name = 'Updated' WHERE id = 10;

MERGE INTO​

提示

该功能为实验功能,自 4.1.0 版本支持。

MERGE INTO 操作用于将源数据与目标 Iceberg 表进行匹配,并根据匹配结果执行插入、更新或删除操作。适合用于数据同步、增量更新等场景。

前提条件:

  • Iceberg 表的格式版本(format-version)必须为 2 或更高版本。V1 格式的表不支持 MERGE INTO 操作。

语法:

MERGE INTO target_table t
USING <source> s
ON <merge_condition>
[WHEN MATCHED [AND <condition>] THEN DELETE]
[WHEN MATCHED [AND <condition>] THEN UPDATE SET <column1> = <value1> [, ...]]
[WHEN NOT MATCHED [AND <condition>] THEN INSERT (<columns>) VALUES (<values>)]

其中 <source> 可以是子查询或另一张表。

示例:

  • 完整匹配操作(包含更新、删除和插入):

    MERGE INTO iceberg_tbl t
    USING (
    SELECT 1 AS id, 'Alice_new' AS name, 26 AS age, 'U' AS flag
    UNION ALL
    SELECT 2, 'Bob', 30, 'D'
    UNION ALL
    SELECT 4, 'Dora', 28, 'I'
    ) s
    ON t.id = s.id
    WHEN MATCHED AND s.flag = 'D' THEN DELETE
    WHEN MATCHED THEN UPDATE SET
    name = s.name,
    age = s.age
    WHEN NOT MATCHED THEN INSERT (id, name, age)
    VALUES (s.id, s.name, s.age);

    上述示例中:

    • id = 2 的行因 flag = 'D' 被删除。
    • id = 1 的行被更新为新的 name 和 age。
    • id = 4 的行因在目标表中不存在而被插入。
  • 仅执行匹配更新:

    MERGE INTO iceberg_tbl t
    USING (SELECT 1 AS id, 'Alice_matched' AS name, 26 AS age) s
    ON t.id = s.id
    WHEN MATCHED THEN UPDATE SET name = s.name, age = s.age;
  • 仅执行不匹配时的插入:

    MERGE INTO iceberg_tbl t
    USING (SELECT 4 AS id, 'Dora' AS name, 40 AS age) s
    ON t.id = s.id
    WHEN NOT MATCHED THEN INSERT (id, name, age) VALUES (s.id, s.name, s.age);
  • 使用子查询作为数据源:

    MERGE INTO iceberg_tbl t
    USING (SELECT id FROM other_table WHERE id = 2) s
    ON t.id = s.id
    WHEN MATCHED THEN UPDATE SET name = 'UpdatedViaSubquery';
  • 分区表的 MERGE INTO 操作:

    MERGE INTO iceberg_partition_tbl t
    USING (
    SELECT 1 AS id, 'Alice_new' AS name, 26 AS age, DATE '2024-01-01' AS dt, 'U' AS flag
    UNION ALL
    SELECT 2, 'Bob', 30, DATE '2024-01-02', 'D'
    UNION ALL
    SELECT 4, 'Dora', 28, DATE '2024-01-04', 'I'
    ) s
    ON t.id = s.id
    WHEN MATCHED AND s.flag = 'D' THEN DELETE
    WHEN MATCHED THEN UPDATE SET
    name = s.name,
    age = s.age
    WHEN NOT MATCHED THEN INSERT (id, name, age, dt)
    VALUES (s.id, s.name, s.age, s.dt);

注意事项:

  1. 以上三种操作(DELETE、UPDATE、MERGE INTO)均要求 Iceberg 表的格式版本(format-version)为 2 或更高版本。如果对 V1 格式的表执行这些操作,会报错:must have format version 2 or higher for position deletes。当执行于 V3 格式表时,系统不仅会使用 Puffin 格式存储 Deletion Vectors 代替原有的 Position Delete 数据,如果涉及内容更新,还会自动调整写入记录的血缘生命周期属性(_last_updated_sequence_number)。
  2. 这些操作支持 Parquet 和 ORC 两种文件格式。
  3. 并发写入场景下,Iceberg 提供了乐观并发控制机制。当发生写入冲突时,操作可能会抛出事务冲突异常。
  4. UPDATE 和 MERGE INTO 中的 UPDATE 操作支持使用表达式和函数,例如 age = age * 2 + 1 或 name = concat(name, '-modified')。
  5. 这些操作同时适用于分区表和非分区表。

相关参数​

  • BE

    参数名称描述默认值
    iceberg_sink_max_file_size最大的数据文件大小。当写入数据量超过该大小后会关闭当前文件,滚动产生一个新文件继续写入1GB
    table_sink_partition_write_max_partition_nums_per_writerBE 节点上每个 Instance 最大写入的分区数目128
    table_sink_non_partition_write_scaling_data_processed_threshold非分区表开始 scaling-write 的数据量阈值。每增加 table_sink_non_partition_write_scaling_data_processed_threshold 数据就会发送给一个新的 writer(instance)进行写入。scaling-write 机制主要是为了根据数据量来使用不同数目的 writer(instance)来进行写入,会随着数据量的增加而增大写入的 writer(instance)数目,从而提高并发写入的吞吐。当数据量比较少的时候也会节省资源,并且尽可能地减少产生的文件数目25MB
    table_sink_partition_write_min_data_processed_rebalance_threshold分区表开始触发重平衡的最少数据量阈值。如果 当前累积的数据量 - 自从上次触发重平衡或者最开始累积的数据量 >= table_sink_partition_write_min_data_processed_rebalance_threshold,就开始触发重平衡机制。如果发现最终生成的文件大小差异过大,可以调小该阈值来增加均衡度。当然过小的阈值会导致重平衡的成本增加,可能会影响性能25MB
    table_sink_partition_write_min_partition_data_processed_rebalance_threshold分区表开始进行重平衡时的最少的分区数据量阈值。如果 当前分区的数据量 >= 阈值 * 当前分区已经分配的 task 数目,就开始对该分区进行重平衡。如果发现最终生成的文件大小差异过大,可以调小该阈值来增加均衡度。当然过小的阈值会导致重平衡的成本增加,可能会影响性能

库表管理​

创建和删除库​

可以通过 SWITCH 语句切换到对应的 Catalog 下,执行 CREATE DATABASE 语句:

SWITCH iceberg;
CREATE DATABASE [IF NOT EXISTS] iceberg_db;

也可以使用全限定名创建,或指定 location(目前只有 hms 类型的 Catalog 支持指定 location),如:

CREATE DATABASE [IF NOT EXISTS] iceberg.iceberg_db;

CREATE DATABASE [IF NOT EXISTS] iceberg.iceberg_db
PROPERTIES ('location'='hdfs://172.21.16.47:4007/path/to/db/');

之后可以通过 SHOW CREATE DATABASE 命令查看 Database 的 Location 信息:

mysql> SHOW CREATE DATABASE iceberg_db;
+-------------+-------------------------------------------------------------------------------------------------+
| Database | Create Database |
+-------------+-------------------------------------------------------------------------------------------------+
| iceberg_db | CREATE DATABASE iceberg_db LOCATION 'hdfs://172.21.16.47:4007/usr/hive/warehouse/iceberg_db.db' |
+----------+----------------------------------------------------------------------------------------------------+

删除库:

DROP DATABASE [IF EXISTS] iceberg.iceberg_db;
警告

对于 Iceberg Database,必须先删除该 Database 下的所有表后,才能删除 Database,否则会报错。

创建和删除表​

  • 创建

    Doris 支持在 Iceberg 中创建分区或非分区表。

    例如:

    -- 创建未分区的 Iceberg 表
    CREATE TABLE unpartitioned_table (
    `col1` BOOLEAN COMMENT 'col1',
    `col2` INT COMMENT 'col2',
    `col3` BIGINT COMMENT 'col3',
    `col4` FLOAT COMMENT 'col4',
    `col5` DOUBLE COMMENT 'col5',
    `col6` DECIMAL(9,4) COMMENT 'col6',
    `col7` STRING COMMENT 'col7',
    `col8` DATE COMMENT 'col8',
    `col9` DATETIME COMMENT 'col9'
    )
    PROPERTIES (
    'write-format'='parquet'
    );

    -- 创建分区的 Iceberg 表
    -- 分区列必须在表的列定义列表中
    CREATE TABLE partition_table (
    `ts` DATETIME COMMENT 'ts',
    `col1` BOOLEAN COMMENT 'col1',
    `col2` INT COMMENT 'col2',
    `col3` BIGINT COMMENT 'col3',
    `col4` FLOAT COMMENT 'col4',
    `col5` DOUBLE COMMENT 'col5',
    `col6` DECIMAL(9,4) COMMENT 'col6',
    `col7` STRING COMMENT 'col7',
    `col8` DATE COMMENT 'col8',
    `col9` DATETIME COMMENT 'col9',
    `pt1` STRING COMMENT 'pt1',
    `pt2` STRING COMMENT 'pt2'
    )
    PARTITION BY LIST (day(ts), pt1, pt2) ()
    PROPERTIES (
    'write-format'='orc',
    'compression-codec'='zlib'
    );

    自 4.1.0 版本开始,Doris 支持创建 Iceberg 表时,指定排序列。并且在写入数据时,会根据指定的排序列进行数据排序,以获得更好的数据查询性能。

    CREATE TABLE ordered_table (
    `id` int NULL,
    `name` text NULL,
    `score` double NULL,
    `create_time` datetimev2(6) NULL
    )
    ORDER BY (`id` ASC NULLS FIRST, `score` DESC NULLS LAST)
    PROPERTIES (
    "write-format" = "parquet",
    "write.parquet.compression-codec" = "zstd"
    );
    • 如不指定排序列,则写入时不做任何排序。
    • 排序的默认规则是 ASC NULLS FIRST。

    创建后,可以通过 SHOW CREATE TABLE 命令查看 Iceberg 的建表语句。关于分区表的分区函数,可以参阅后面的【分区】小节。

  • 删除

    可以通过 DROP TABLE 语句删除一个 Iceberg 表。当前删除表后,会同时删除数据,包括分区数据。

    例如:

    DROP TABLE [IF EXISTS] iceberg_tbl;
  • 列类型映射

    参考【列类型映射】部分。

  • 分区

    Iceberg 中的分区类型对应 Doris 中的 List 分区。因此,在 Doris 中创建 Iceberg 分区表,需使用 List 分区的建表语句,但无需显式地枚举各个分区。在写入数据时,Doris 会根据数据的值,自动创建对应的 Iceberg 分区。

    • 支持创建单列或多列分区表。

    • 支持分区转换函数来支持 Iceberg 隐式分区以及分区演进的功能。具体 Iceberg 分区转换函数可以查看 Iceberg partition transforms。

      • year(ts) 或者 years(ts)

      • month(ts) 或者 months(ts)

      • day(ts) 或者 days(ts) 或者 date(ts)

      • hour(ts) 或者 hours(ts) 或者 date_hour(ts)

      • bucket(N, col)

      • truncate(L, col)

  • 文件格式

    • Parquet(默认)

      注意,由 Doris 创建的 Iceberg 表,Datetime 对应的是 timestamp_ntz 类型。

      3.1.0 之后的版本中,Datetime 类型写入到 Parquet 文件时,物理类型使用的是 INT64 而非 INT96。

      此外,如果是其他系统创建的 Iceberg 表,虽然 timestamp 和 timestamp_ntz 类型都映射为 Doris 的 Datetime 类型。但在写入时,会根据实际类型判断是否需要处理时区。

    • ORC

  • 压缩格式

    • Parquet:snappy、zstd(默认)、plain(plain 即不采用压缩)。

    • ORC:snappy、zlib(默认)、zstd、plain(plain 即不采用压缩)。

  • 存储介质

    • HDFS

    • 对象存储

Schema 变更​

自 3.1.0 版本开始,Doris 支持 Iceberg 表的 Schema 变更(Schema Change),可以通过 ALTER TABLE 语句来修改表的 Schema。

支持的 Schema 变更操作包括:

  • 修改列名称

    通过 RENAME COLUMN 子句修改列名称,不支持修改嵌套类型中的列名称。

    ALTER TABLE iceberg_table RENAME COLUMN old_col_name TO new_col_name;
  • 添加一列

    通过 ADD COLUMN 添加新列,不支持为嵌套类型添加新列。

    在添加新列时,可以指定 nullable 属性、默认值、注释和列位置。

    ALTER TABLE iceberg_table ADD COLUMN col_name col_type [NULL|NOT NULL, [DEFAULT default_value, [COMMENT 'comment', [FIRST|AFTER col_name]]]];

    示例:

    ALTER TABLE iceberg_table ADD COLUMN new_col STRING NOT NULL DEFAULT 'default_value' COMMENT 'This is a new col' AFTER old_col;
  • 添加多列

    可以通过 ADD COLUMN 添加多列,新列会被添加到表的末尾,不支持指定列位置,不支持为嵌套类型添加新列。

    每一列的语法和添加单列时一样。

    ALTER TABLE iceberg_table ADD COLUMN (col_name1 col_type1 [NULL|NOT NULL, [DEFAULT default_value, [COMMENT 'comment']]], col_name2 col_type2 [NULL|NOT NULL, [DEFAULT default_value, [COMMENT 'comment']]] ...);
  • 删除列

    通过 DROP COLUMN 删除列,不支持删除嵌套类型中的列。

    ALTER TABLE iceberg_table DROP COLUMN col_name;
  • 修改列

    通过 MODIFY COLUMN 语句修改列的属性,包括类型、nullable、默认值、注释和列位置。

    自 4.0.4 版本起,Doris 支持修改复杂类型(STRUCT、ARRAY、MAP),包括安全的类型推导和追加 Struct 字段。

    嵌套类型中支持的安全类型推导:

    • INT -> BIGINT, LARGEINT
    • TINYINT -> SMALLINT, INT, BIGINT, LARGEINT
    • SMALLINT -> INT, BIGINT, LARGEINT
    • BIGINT -> LARGEINT
    • FLOAT -> DOUBLE
    • VARCHAR(n) -> VARCHAR(m) 其中 m > n

    修改复杂类型的限制:

    • 所有新的嵌套字段必须为 nullable。
    • 不能将可选(optional)改为必填(required)。
    • 复杂类型的默认值仅支持 NULL。

    注意:修改列的属性时,所有没有被修改的属性也应该显式地指定为原来的值。

    ALTER TABLE iceberg_table MODIFY COLUMN col_name col_type [NULL|NOT NULL, [DEFAULT default_value, [COMMENT 'comment', [FIRST|AFTER col_name]]]];

    示例:

    CREATE TABLE iceberg_table (
    id INT,
    name STRING
    );
    -- 修改 id 列的类型为 BIGINT,设置为 NOT NULL,默认值为 0,并添加注释
    ALTER TABLE iceberg_table MODIFY COLUMN id BIGINT NOT NULL DEFAULT 0 COMMENT 'This is a modified id column' FIRST;

    修改复杂类型的示例:

    -- 创建包含复杂类型的 Iceberg 表
    CREATE TABLE iceberg_tbl (
    id BIGINT,
    user_info STRUCT<name:STRING, scores:ARRAY<INT>, age:INT>,
    dt STRING
    );

    -- 为 STRUCT 类型的列追加新字段 (email)
    ALTER TABLE iceberg_tbl MODIFY COLUMN user_info STRUCT<name:STRING, scores:ARRAY<INT>, age:INT, email:STRING>;

    -- 将嵌套 ARRAY 元素类型从 INT 提升为 BIGINT
    ALTER TABLE iceberg_tbl MODIFY COLUMN user_info STRUCT<name:STRING, scores:ARRAY<BIGINT>, age:INT, email:STRING>;
  • 重新排序

    通过 ORDER BY 重新排序列,指定新的列顺序。

    ALTER TABLE iceberg_table ORDER BY (col_name1, col_name2, ...);

Partition Evolution​

自 4.0.2 版本开始,Doris 支持通过 ALTER 语句对 Iceberg 表进行 Partition Evolution。

支持的分区变换包括:

变换语法示例
bucketbucket(N, column)bucket(16, id)
truncatetruncate(N, column)truncate(10, name)
yearyear(column)year(ts)
monthmonth(column)month(ts)
dayday(column)day(ts)
hourhour(column)hour(ts)
identitycolumncategory

支持的操作包括:

  • 增加分区键

    -- 使用可选的 AS 关键字为分区字段指定自定义名称
    ALTER TABLE table_name ADD PARTITION KEY partition_transform [AS key_name];

    -- 示例
    ALTER TABLE prod.db.sample ADD PARTITION KEY bucket(16, id);
    ALTER TABLE prod.db.sample ADD PARTITION KEY truncate(4, data);
    ALTER TABLE prod.db.sample ADD PARTITION KEY year(ts);
    -- 使用可选的 AS 关键字为分区字段指定自定义名称
    ALTER TABLE prod.db.sample ADD PARTITION KEY bucket(16, id) AS shard;
  • 删除分区键

    ALTER TABLE table_name DROP PARTITION KEY partition_transform|key_name;

    -- 示例
    ALTER TABLE prod.db.sample DROP PARTITION KEY catalog;
    ALTER TABLE prod.db.sample DROP PARTITION KEY bucket(16, id);
    ALTER TABLE prod.db.sample DROP PARTITION KEY truncate(4, data);
    ALTER TABLE prod.db.sample DROP PARTITION KEY year(ts);
    ALTER TABLE prod.db.sample DROP PARTITION KEY shard;
  • 替换分区键

    -- 使用可选的 AS 关键字为分区字段指定自定义名称
    ALTER TABLE table_name REPLACE PARTITION KEY key_name WITH partition_transform [AS key_name];

    -- 示例
    ALTER TABLE prod.db.sample REPLACE PARTITION KEY ts_day WITH day(ts);
    -- 使用可选的 AS 关键字为新分区字段指定自定义名称
    ALTER TABLE prod.db.sample REPLACE PARTITION KEY ts_day WITH day(ts) AS day_of_ts;

管理 Branch & Tag​

该功能自 3.1.0 版本支持

  • 创建 Branch

    语法:

    ALTER TABLE [catalog.][database.]table_name
    CREATE [OR REPLACE] BRANCH [IF NOT EXISTS] <branch_name>
    [AS OF VERSION <snapshot_id>]
    [RETAIN <num> { DAYS | HOURS | MINUTES }]
    [WITH SNAPSHOT RETENTION { snapshotKeep | timeKeep }]

    snapshotKeep:
    <num> SNAPSHOTS [<num> { DAYS | HOURS | MINUTES }]

    timeKeep:
    <num> { DAYS | HOURS | MINUTES }

    示例:

    -- 创建分支 "b1"。
    ALTER TABLE tbl CREATE BRANCH b1;
    ALTER TABLE tb1 CREATE BRANCH IF NOT EXISTS b1;
    -- 创建或替换分支 "b1"。
    ALTER TABLE tb1 CREATE OR REPLACE BRANCH b1;
    -- 基于快照 "123456" 创建或替换分支 "b1"。
    ALTER TABLE tb1 CREATE OR REPLACE BRANCH b1 AS OF VERSION 123456;
    -- 基于快照 "123456" 创建或替换分支 "b1",分支保留 1 天。
    ALTER TABLE tb1 CREATE OR REPLACE BRANCH b1 AS OF VERSION 123456 RETAIN 1 DAYS;
    -- 基于快照 "123456" 创建分支 "b1",分支保留 30 天。分支中的保留最近的 3 个快照。
    ALTER TABLE tb1 CREATE BRANCH b1 AS OF VERSION 123456 RETAIN 30 DAYS WITH SNAPSHOT RETENTION 3 SNAPSHOTS;
    -- 基于快照 "123456" 创建分支 "b1",分支保留 30 天。分支中的快照最多保留 2 天。
    ALTER TABLE tb1 CREATE BRANCH b1 AS OF VERSION 123456 RETAIN 30 DAYS WITH SNAPSHOT RETENTION 2 DAYS;
    -- 基于快照 "123456" 创建分支 "b1",分支保留 30 天。分支中的保留最近的 3 个快照,分支中的快照最多保留 2 天。
    ALTER TABLE tb1 CREATE BRANCH b1 AS OF VERSION 123456 RETAIN 30 DAYS WITH SNAPSHOT RETENTION 3 SNAPSHOTS 2 DAYS;
  • 删除 Branch

    语法:

    ALTER TABLE [catalog.][database.]table_name
    DROP BRANCH [IF EXISTS] <branch_name>;

    示例:

    ALTER TABLE tbl DROP BRANCH b1;
  • 创建 Tag

    语法:

    ALTER TABLE [catalog.][database.]table_name
    CREATE [OR REPLACE] TAG [IF NOT EXISTS] <tag_name>
    [AS OF VERSION <snapshot_id>]
    [RETAIN <num> { DAYS | HOURS | MINUTES }]

    示例:

    -- 创建标记 "t1"。
    ALTER TABLE tbl CREATE TAG t1;
    ALTER TABLE tb1 CREATE TAG IF NOT EXISTS t1;
    -- 创建或替换标记 "t1"。
    ALTER TABLE tb1 CREATE OR REPLACE TAG t1;
    -- 基于快照 "123456" 创建或替换标记 "t1"。
    ALTER TABLE tb1 CREATE OR REPLACE TAG b1 AS OF VERSION 123456;
    -- 基于快照 "123456" 创建或替换标记 "b1",标记保留 1 天。
    ALTER TABLE tb1 CREATE OR REPLACE TAG b1 AS OF VERSION 123456 RETAIN 1 DAYS;
  • 删除 Tag

    语法:

    ALTER TABLE [catalog.][database.]table_name
    DROP TAG [IF EXISTS] <tag_name>;

    示例:

    ALTER TABLE tbl DROP TAG t1;

Iceberg 表操作​

该功能自 4.0.2 版本开始支持。具体支持版本参见各个操作的说明。

该功能为实验性功能。

Doris 支持通过 ALTER TABLE EXECUTE 语法来执行 Iceberg 表的特定操作(Actions)。该语法提供了统一的接口来执行表优化和快照管理等各种操作。

ALTER TABLE [catalog.][database.]table_name
EXECUTE <action_name> ("key1" = "value1", "key2" = "value2", ...)
[WHERE <condition>]
  • action_name:要执行的操作名称。
  • ("key1" = "value1", ...):操作相关的参数,以键值对形式提供。参数名称需要使用双引号包裹。
  • [WHERE <condition>]:可选的 WHERE 条件,用于指定操作的分区或数据范围(仅部分操作支持)。

cherrypick_snapshot​

cherrypick_snapshot 操作用于将指定快照的变更合并到当前表状态,创建一个新的快照而不修改或删除原始快照。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE cherrypick_snapshot ("snapshot_id" = "<snapshot_id>")

参数说明:

参数名称类型必填描述
snapshot_idLong是要合并的快照 ID

返回值:

执行 cherrypick_snapshot 操作会返回一个结果集,包含以下 2 列:

列名类型描述
source_snapshot_idBIGINT被合并的快照 ID
current_snapshot_idBIGINT合并操作后新创建并设置为当前快照的快照 ID

示例:

-- 将快照 123456789 的变更合并到当前表状态
ALTER TABLE iceberg_db.iceberg_table
EXECUTE cherrypick_snapshot ("snapshot_id" = "123456789");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. 如果指定的快照不存在,操作会失败。
  3. 合并操作会创建一个新的快照,不会删除原始快照。

expire_snapshots​

expire_snapshots 操作用于删除 Iceberg 表的旧快照,以释放存储空间并提高元数据性能。该操作遵循 Apache Iceberg Spark 过程规范。

支持版本:4.1.0+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE expire_snapshots ("key1" = "value1", "key2" = "value2", ...)

参数说明:

参数名称类型必填描述
older_thanString否快照过期的时间阈值,早于该时间的快照将被删除。支持 ISO 日期时间格式(如 2024-01-01T00:00:00)或毫秒时间戳格式
retain_lastInteger否保留的祖先快照数量。当单独指定时,自动将 older_than 设置为当前时间
snapshot_idsString否要过期的特定快照 ID 列表,以逗号分隔
max_concurrent_deletesInteger否执行删除操作的线程池大小
clean_expired_metadataBoolean否设置为 true 时,清理未使用的分区规格和 Schema

返回值:

执行 expire_snapshots 操作会返回一个结果集,包含以下 6 列:

列名类型描述
deleted_data_files_countBIGINT已删除的数据文件数量
deleted_position_delete_files_countBIGINT已删除的 Position Delete 文件数量
deleted_equality_delete_files_countBIGINT已删除的 Equality Delete 文件数量
deleted_manifest_files_countBIGINT已删除的 Manifest 文件数量
deleted_manifest_lists_countBIGINT已删除的 Manifest List 文件数量
deleted_statistics_files_countBIGINT已删除的统计文件数量

示例:

-- 过期快照,只保留最近的 2 个
ALTER TABLE iceberg_db.iceberg_table
EXECUTE expire_snapshots ("retain_last" = "2");

-- 过期指定时间之前的快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE expire_snapshots ("older_than" = "2024-01-01T00:00:00");

-- 过期指定 ID 的快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE expire_snapshots ("snapshot_ids" = "123456789,987654321");

-- 组合参数:过期 2024-06-01 之前的快照,但至少保留最近的 5 个
ALTER TABLE iceberg_db.iceberg_table
EXECUTE expire_snapshots ("older_than" = "2024-06-01T00:00:00", "retain_last" = "5");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. 如果同时指定 older_than 和 retain_last,则两个条件都会生效:只有早于 older_than 且不在最近 retain_last 个快照中的快照才会被删除。
  3. snapshot_ids 可以单独使用,用于删除特定的快照。
  4. 该操作会永久删除快照及其关联的数据文件,请谨慎使用。
  5. 建议在执行前先查询 $snapshots 系统表了解表的快照信息。

fast_forward​

fast_forward 操作用于将一个分支的当前快照快速推进到另一个分支的最新快照。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE fast_forward ("branch" = "<branch_name>", "to" = "<target_branch>")

参数说明:

参数名称类型必填描述
branchString是要推进的分支名称
toString是目标分支名称,会将 branch 推进到该分支的最新快照

返回值:

执行 fast_forward 操作会返回一个结果集,包含以下 3 列:

列名类型描述
branch_updatedSTRING被推进的分支名称
previous_refBIGINT推进操作前分支指向的快照 ID(可为 NULL)
updated_refBIGINT推进操作后分支指向的快照 ID

示例:

-- 将 feature 分支推进到 main 分支的最新快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE fast_forward ("branch" = "feature", "to" = "main");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. 如果指定的分支不存在,操作会失败。
  3. 只能推进分支,不能推进标签(Tag)。

publish_changes​

publish_changes 操作是“写入 - 审核 - 发布” (Write-Audit-Publish) 模式中的“发布”步骤。该过程会查找带有特定 wap.id 属性的快照,并将其添加到当前表状态中。这样,用户就可以在验证后原子性地使“暂存”数据可见。

支持版本:4.1.0+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE publish_changes ("wap_id" = "<wap-id>")

参数说明:

参数名称类型必填描述
wap_idString是要进行发布的快照的 wap.id

返回值:

执行 publish_changes 操作会返回一个结果集,包含以下 2 列:

列名类型描述
previous_snapshot_idBIGINT发布之前的快照 ID
current_snapshot_idBIGINT发布之后的新的快照 ID

示例:

ALTER TABLE iceberg_db.iceberg_table
EXECUTE publish_changes ("wap_id" = "branch-123")

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. 如果指定的 wap.id 不存在,操作会失败。

rewrite_data_files​

rewrite_data_files 操作用于对 Iceberg 表进行数据文件重写,实现小文件合并(Compaction)功能,以优化查询性能和存储效率。该操作是一个同步操作。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE rewrite_data_files ("key1" = "value1", "key2" = "value2", ...) [WHERE <condition>]

参数说明:

参数名称类型默认值描述
target-file-size-bytesLong536870912 (512MB)目标文件大小(字节)。输出文件的目标大小
min-file-size-bytesLongtarget-file-size-bytes * 0.75最小文件大小(字节)。小于此大小的文件会被重写。如果未指定,默认为目标文件大小的 75%
max-file-size-bytesLongtarget-file-size-bytes * 1.8最大文件大小(字节)。大于此大小的文件会被拆分。如果未指定,默认为目标文件大小的 180%
min-input-filesInteger5触发重写的最小输入文件数。只有当需要重写的文件数达到此阈值时才会执行重写
rewrite-allBooleanfalse是否重写所有文件,无论文件大小
max-file-group-size-bytesLong107374182400 (100GB)单个文件组的最大大小(字节),用于限制单个重写任务处理的数据量
delete-file-thresholdIntegerInteger.MAX_VALUE触发重写所需的最小删除文件数
delete-ratio-thresholdDouble0.3触发重写所需的最小删除记录比例(删除记录数 / 总记录数),范围:0.0 - 1.0
output-spec-idLong2输出文件的分区规范 ID

返回值:

执行 rewrite_data_files 操作会返回一个结果集,包含以下 4 列:

列名类型描述
rewritten_data_files_countINT被重写的数据文件数量
added_data_files_countINT新增的数据文件数量
rewritten_bytes_countINT重写的数据字节数
removed_delete_files_countBIGINT被移除的删除文件数量

示例:

-- 使用默认参数进行数据文件重写
ALTER TABLE iceberg_db.iceberg_table EXECUTE rewrite_data_files();

-- 指定目标文件大小和最小输入文件数
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rewrite_data_files (
"target-file-size-bytes" = "134217728",
"min-input-files" = "10"
);

-- 使用 WHERE 条件重写特定分区的数据
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rewrite_data_files (
"target-file-size-bytes" = "268435456"
)
WHERE date_col = '2024-01-01';

-- 重写所有文件
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rewrite_data_files (
"rewrite-all" = "true",
"target-file-size-bytes" = "536870912"
);

注意事项:

  1. rewrite_data_files 操作会读取数据文件并重新写入,会产生额外的 I/O 和计算开销,请合理分配集群资源。
  2. 执行前可以通过查看数据文件分布章节中的 SQL 来评估是否需要执行重写操作。
  3. WHERE 条件可用于限制重写的分区或数据范围,这个条件会过滤掉那些不包含符合 WHERE 条件的数据的文件,从而减少重写的文件数量和数据量。
  4. 执行前可以通过重写文件选择逻辑章节中的 SQL 来计算哪些文件会被重写。

rewrite_manifests​

rewrite_manifests 操作允许用户通过重写清单文件(Manifest File)来优化 Iceberg 表元数据,从而提高查询性能并减少元数据开销。

这解决了大型 Iceberg 表中清单文件优化的问题,因为大量小型清单文件可能会影响查询计划性能。

支持版本:4.1.0+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE rewrite_manifests();

参数说明:

无参数

返回值:

执行 rewrite_manifests 操作会返回一个结果集,包含以下 2 列:

列名类型描述
rewritten_manifests_countBIGINT被重写的文件数量
total_data_manifests_countBIGINT被重写的数据清单文件数量

示例:

ALTER TABLE iceberg_db.iceberg_table
EXECUTE rewrite_manifests();

rollback_to_snapshot​

rollback_to_snapshot 操作用于将 Iceberg 表回滚到指定的快照。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE rollback_to_snapshot ("snapshot_id" = "<snapshot_id>")

参数说明:

参数名称类型必填描述
snapshot_idLong是要回滚到的快照 ID

返回值:

执行 rollback_to_snapshot 操作会返回一个结果集,包含以下 2 列:

列名类型描述
previous_snapshot_idBIGINT回滚操作前当前快照的 ID
current_snapshot_idBIGINT回滚操作后设置为当前快照的快照 ID

示例:

-- 回滚到快照 987654321
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rollback_to_snapshot ("snapshot_id" = "987654321");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. 如果指定的快照不存在,操作会失败。
  3. 如果当前快照已经是目标快照,操作会直接返回,不会创建新快照。

rollback_to_timestamp​

rollback_to_timestamp 操作用于将 Iceberg 表回滚到指定时间点的快照。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE rollback_to_timestamp ("timestamp" = "<timestamp>")

参数说明:

参数名称类型必填描述
timestampString是要回滚到的时间点。支持两种格式:
1. ISO 日期时间格式:yyyy-MM-dd HH:mm:ss.SSS(如 2024-01-01 10:30:00.000)
2. 毫秒时间戳(如 1704067200000)

返回值:

执行 rollback_to_timestamp 操作会返回一个结果集,包含以下 2 列:

列名类型描述
previous_snapshot_idBIGINT回滚操作前当前快照的 ID
current_snapshot_idBIGINT指定时间点时的快照 ID,现在设置为当前快照

示例:

-- 回滚到指定时间点(ISO 格式)
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rollback_to_timestamp ("timestamp" = "2024-01-01 10:30:00.000");

-- 回滚到指定时间点(时间戳格式)
ALTER TABLE iceberg_db.iceberg_table
EXECUTE rollback_to_timestamp ("timestamp" = "1704067200000");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. timestamp 参数支持 ISO 日期时间格式(yyyy-MM-dd HH:mm:ss.SSS)或毫秒时间戳格式。
  3. 如果指定的时间点没有对应的快照,操作会失败。

set_current_snapshot​

set_current_snapshot 操作用于将 Iceberg 表的当前快照设置为指定的快照 ID 或引用(分支或标签)。

支持版本:4.0.2+

语法:

ALTER TABLE [catalog.][database.]table_name
EXECUTE set_current_snapshot ("snapshot_id" = "<snapshot_id>" | "ref" = "<ref_name>")

参数说明:

参数名称类型必填描述
snapshot_idLong二选一要设置为当前快照的快照 ID
refString二选一要设置为当前快照的引用名称(分支或标签)

返回值:

执行 set_current_snapshot 操作会返回一个结果集,包含以下 2 列:

列名类型描述
previous_snapshot_idBIGINT设置操作前当前快照的 ID
current_snapshot_idBIGINT设置操作后当前快照的 ID(来自 snapshot_id 参数或从 ref 参数解析得到)

示例:

-- 通过快照 ID 设置当前快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE set_current_snapshot ("snapshot_id" = "123456789");

-- 通过分支名称设置当前快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE set_current_snapshot ("ref" = "feature_branch");

-- 通过标签名称设置当前快照
ALTER TABLE iceberg_db.iceberg_table
EXECUTE set_current_snapshot ("ref" = "v1.0");

注意事项:

  1. 该操作不支持 WHERE 条件。
  2. snapshot_id 和 ref 参数互斥,只能指定其中一个。
  3. 如果指定的快照 ID 或引用不存在,操作会失败。
  4. 如果当前快照已经是目标快照,操作会直接返回,不会创建新快照。

Iceberg 表优化​

查看数据文件分布​

通过以下 SQL 可以分析 Iceberg 表的数据分布和 delete 文件数量等,从而判断是否需要进行优化,如 rewrite_data_files。

  • 显示数据文件大小分布。可以判断是否有过多的小文件。

    SELECT
    CASE
    WHEN file_size_in_bytes BETWEEN 0 AND 8 * 1024 * 1024 THEN '0-8M'
    WHEN file_size_in_bytes BETWEEN 8 * 1024 * 1024 + 1 AND 32 * 1024 * 1024 THEN '8-32M'
    WHEN file_size_in_bytes BETWEEN 32 * 1024 * 1024 + 1 AND 128 * 1024 * 1024 THEN '32-128M'
    WHEN file_size_in_bytes BETWEEN 128 * 1024 * 1024 + 1 AND 512 * 1024 * 1024 THEN '128-512M'
    WHEN file_size_in_bytes > 512 * 1024 * 1024 THEN '> 512M'
    ELSE 'Unknown'
    END AS SizeRange,
    COUNT(*) AS FileNum
    FROM store_sales$data_files
    GROUP BY
    SizeRange;

    +-----------+---------+
    | SizeRange | FileNum |
    +-----------+---------+
    | 0-8M | 8 |
    | 8-32M | 6 |
    +-----------+---------+
  • 显示数据文件、Delete 文件的数量

    SELECT
    CASE
    WHEN content = 0 THEN 'DataFile'
    WHEN content = 1 THEN 'PositionDeleteFile'
    WHEN content = 2 THEN 'EqualityDeleteFile'
    ELSE 'Unknown'
    END AS ContentType,
    COUNT(*) AS FileNum,
    SUM(file_size_in_bytes) AS SizeInBytes,
    SUM(record_count) AS Records
    FROM
    iceberg_table$files
    GROUP BY
    ContentType;

    +--------------------+---------+-------------+---------+
    | ContentType | FileNum | SizeInBytes | Records |
    +--------------------+---------+-------------+---------+
    | EqualityDeleteFile | 2787 | 1432518 | 27870 |
    | DataFile | 2787 | 4062416 | 38760 |
    | PositionDeleteFile | 11 | 36608 | 10890 |
    +--------------------+---------+-------------+---------+

Dangling Delete​

某些情况下,在执行完 rewrite_data_files 方法后,某些 Position Delete 的引用可能没有从 Snapshot 元数据中删除(Dangling Delete)。此时如果直接使用元数据中的行数信息,结果可能是错误的。

因此,在默认情况下,对于 COUNT(*) 查询,如果发现存在 Position Delete 文件,则不启用 COUNT 下推优化,而是直接读取文件获取真实的 COUNT(*) 结果。但这种方式耗时较长。

如果用户能确保没有 Dangling Delete 问题,则可以通过 Doris 会话变量 ignore_iceberg_dangling_delete 来跳过这个检查。该变量默认为 false。当设置为 true 时,则会直接通过元数据中的行数信息,返回 COUNT(*) 的结果,提升查询效率。

该功能自 3.1.4 和 4.0.3 版本支持。

附录​

rewrite_data_files 文件选择策略​

你可以查看此章节来了解被重写文件的选择逻辑。也可以使用以下 SQL 查询来基于系统表信息分析哪些文件会被重写。这有助于你在执行实际重写操作之前进行调试和理解重写行为。

重写操作采用两阶段过滤和分组策略:

  1. 阶段 1:文件级别过滤

    首先基于文件级别条件过滤文件。只有满足以下至少一个条件的文件才会被考虑重写:

    1. 文件大小超出范围:文件小于 min-file-size-bytes 或大于 max-file-size-bytes
    2. 删除文件过多:关联的删除文件数量 >= delete-file-threshold
    3. 删除比例过高:删除记录比例 >= delete-ratio-threshold

    关于删除文件条件的说明: 与删除文件相关的条件(delete-file-threshold 和 delete-ratio-threshold)会影响文件重写选择,但无法通过系统表 SQL 查询准确计算。这是因为当前系统表信息不足以准确判断哪些数据文件受到删除文件的影响。删除文件可能只影响特定的数据文件(特别是 Position Delete 文件),删除文件与数据文件的关联需要更详细的元数据,这些信息在当前系统表中不可用。实际的重写操作将基于系统表中未暴露的更详细元数据来评估这些条件。

  2. 阶段 2:BinPack 分组和组级别过滤

    文件级别过滤后,文件按分区分组,然后在每个分区内使用 BinPack 算法基于文件大小和 max-file-group-size-bytes 对文件进行分组。BinPack 算法会智能地将文件打包成组以优化重写操作。

    分组后,文件组会根据组级别条件进行过滤。文件组满足以下至少一个条件时会被重写:

    1. 足够的输入文件:组内文件数 > 1 且文件数 >= min-input-files
    2. 足够的内容:组内文件数 > 1 且总大小 > target-file-size-bytes
    3. 内容过多:组总大小 > max-file-group-size-bytes
    4. 包含有删除文件问题的文件:组内至少有一个文件有太多删除文件或删除比例过高。

    关于 BinPack 分组的说明: 用于在每个分区内对文件进行分组的 BinPack 算法是一个复杂的优化算法,无法通过系统表 SQL 查询准确模拟。该算法会考虑文件大小和 max-file-group-size-bytes 来智能地将文件打包成组,这可能导致与简单的按分区分组不同的分组结果。因此,下面提供的 SQL 查询只能识别满足文件级别条件的文件,但无法准确计算最终会被重写的文件组。

下面给出一些查询策略示例:

  • 查询 1:识别满足文件级别条件的文件

    该查询识别满足文件级别条件(文件大小)并将被考虑重写的文件:

    -- 设置参数(根据实际的重写参数调整)
    SET @min_file_size_bytes = 16 * 1024 * 1024; -- 16 MB(默认:target-file-size-bytes * 0.75)
    SET @max_file_size_bytes = 768 * 1024 * 1024; -- 768 MB(默认:target-file-size-bytes * 1.8)

    SELECT
    file_path,
    `partition`,
    file_size_in_bytes / 1024.0 / 1024.0 AS file_size_mb,
    record_count,
    CASE
    WHEN file_size_in_bytes < @min_file_size_bytes THEN 'Too small'
    WHEN file_size_in_bytes > @max_file_size_bytes THEN 'Too large'
    END AS size_issue
    FROM iceberg_table$data_files
    WHERE file_size_in_bytes < @min_file_size_bytes
    OR file_size_in_bytes > @max_file_size_bytes
    ORDER BY `partition`, file_size_in_bytes DESC;

    示例输出:

    +----------------------------------------------------------------------------------------+---------------------------+-------------+--------------+------------+
    | file_path | partition | file_size_mb | record_count | size_issue |
    +----------------------------------------------------------------------------------------+---------------------------+-------------+--------------+------------+
    | s3a://bucket/path/to/table/data/date_col=2024-01-01/00000-0-00000-00000-0.parquet | {"date_col":"2024-01-01"} | 0.00215 | 5 | Too small |
    | s3a://bucket/path/to/table/data/date_col=2024-01-01/00001-0-00001-00001-0.parquet | {"date_col":"2024-01-01"} | 0.00198 | 5 | Too small |
    | s3a://bucket/path/to/table/data/date_col=2024-01-02/00002-0-00002-00002-0.parquet | {"date_col":"2024-01-02"} | 0.00231 | 5 | Too small |
    | s3a://bucket/path/to/table/data/date_col=2024-01-02/00003-0-00003-00003-0.parquet | {"date_col":"2024-01-02"} | 850.23456 | 1250000 | Too large |
    | s3a://bucket/path/to/table/data/date_col=2024-01-03/00004-0-00004-00004-0.parquet | {"date_col":"2024-01-03"} | 0.00245 | 5 | Too small |
    +----------------------------------------------------------------------------------------+---------------------------+-------------+--------------+------------+
    5 rows in set

    注意: 此查询只能识别满足文件级别条件(文件大小)的文件。实际会被重写的文件组取决于 BinPack 分组算法,该算法无法通过 SQL 查询准确模拟。此查询识别的文件可能会被 BinPack 算法以不同方式分组,并且某些组可能会根据组级别条件被过滤掉。

  • 查询 2:总体统计摘要

    该查询提供关于满足文件级别条件文件的总体统计信息:

    -- 设置参数(根据你的重写参数调整)
    SET @min_file_size_bytes = 16 * 1024 * 1024;
    SET @max_file_size_bytes = 768 * 1024 * 1024;

    WITH file_analysis AS (
    SELECT
    `partition`,
    file_path,
    file_size_in_bytes,
    record_count,
    (file_size_in_bytes < @min_file_size_bytes OR file_size_in_bytes > @max_file_size_bytes) AS meets_file_level_conditions
    FROM iceberg_table$data_files
    )
    SELECT
    'Total files' AS metric,
    COUNT(*) AS value
    FROM file_analysis
    UNION ALL
    SELECT
    'Files meeting file-level conditions',
    SUM(CASE WHEN meets_file_level_conditions THEN 1 ELSE 0 END)
    FROM file_analysis
    UNION ALL
    SELECT
    'Total size (GB)',
    ROUND(SUM(file_size_in_bytes) / 1024.0 / 1024.0 / 1024.0, 2)
    FROM file_analysis
    UNION ALL
    SELECT
    'Size meeting file-level conditions (GB)',
    ROUND(SUM(CASE WHEN meets_file_level_conditions THEN file_size_in_bytes ELSE 0 END) / 1024.0 / 1024.0 / 1024.0, 2)
    FROM file_analysis
    UNION ALL
    SELECT
    'Percentage meeting file-level conditions (%)',
    ROUND(SUM(CASE WHEN meets_file_level_conditions THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2)
    FROM file_analysis;

    示例输出:

    +----------------------------------------------+--------+
    | metric | value |
    +----------------------------------------------+--------+
    | Total files | 15.00 |
    | Total size (GB) | 2.45 |
    | Files meeting file-level conditions | 12.00 |
    | Size meeting file-level conditions (GB) | 1.85 |
    | Percentage meeting file-level conditions (%) | 80.00 |
    +----------------------------------------------+--------+
    5 rows in set

    注意事项:

    • 替换示例中的 iceberg_table 为实际的表名。
    • 根据实际的重写参数调整参数值(@min_file_size_bytes、@max_file_size_bytes)。
    • 上面显示的默认值对应于默认重写参数(min-file-size-bytes = target-file-size-bytes * 0.75,max-file-size-bytes = target-file-size-bytes * 1.8)。
  • 查看快照和分支对应情况

    SELECT
    refs_data.snapshot_id,
    snapshots.committed_at,
    snapshots.operation,
    ARRAY_SORT(refs_data.refs)
    FROM (
    SELECT
    snapshot_id,
    ARRAY_AGG(CONCAT(type, ':', name)) AS refs
    FROM
    iceberg_table$refs
    GROUP BY
    snapshot_id
    ) AS refs_data
    JOIN (
    SELECT
    snapshot_id,
    committed_at,
    operation
    FROM
    iceberg_table$snapshots
    ) AS snapshots
    ON refs_data.snapshot_id = snapshots.snapshot_id
    ORDER BY
    snapshots.committed_at;

    +---------------------+----------------------------+-----------+-------------------------------------+
    | snapshot_id | committed_at | operation | ARRAY_SORT(refs_data.refs) |
    +---------------------+----------------------------+-----------+-------------------------------------+
    | 8272911997874079853 | 2025-07-10 15:27:07.177000 | append | ["BRANCH:b1", "TAG:t1"] |
    | 1325777059626757917 | 2025-07-10 15:27:07.530000 | append | ["BRANCH:b2", "TAG:t2"] |
    | 76492482642020578 | 2025-07-10 15:27:07.865000 | append | ["BRANCH:b3", "TAG:t3"] |
    | 1788715857849070138 | 2025-07-12 04:15:19.626000 | append | ["BRANCH:main", "TAG:t4", "TAG:t5"] |
    +---------------------+----------------------------+-----------+-------------------------------------+