Iceberg外表(XIHE SQL)

更新时间:
复制 MD 格式

云原生数据仓库 AnalyticDB MySQL 版XIHE引擎原生支持Apache Iceberg湖表格式。您可以通过标准SQL创建Iceberg表,并执行数据写入、查询操作。

前提条件

  • 集群的产品系列为企业版、基础版或湖仓版。

  • 集群的内核版本为3.2.3.0及以上版本。

  • 如需使用内湖模式(AnalyticDB for MySQL托管湖存储),需提交工单开通湖存储功能。详情请参见湖存储

背景信息

Apache Iceberg是一种开放的数据湖表格式,支持ACID事务、分区转换等特性。数据以Parquet格式存储在OSS上,任何兼容Iceberg的计算引擎均可直接读取。

AnalyticDB for MySQL支持两种存储模式,在建表时一次性确定,建表后不可变更:

维度

内湖(托管湖存储)

外湖(用户自有OSS)

存储管理

AnalyticDB for MySQL全托管

用户自管理OSS Bucket

建表关键参数

catalog_type='ADB' + adb_lake_bucket

LOCATION 'oss://...'

开通方式

需提交工单申请开通

同账号下无需额外操作

适用场景

新项目,希望简化运维

已有OSS数据,或需要自主管理存储

建表

语法

CREATE TABLE [IF NOT EXISTS] <db>.<table> (
    <col1>  <type1>  [COMMENT '<comment>'],
    <col2>  <type2>  [COMMENT '<comment>'],
    ...
)
[COMMENT '<table_comment>']
[PARTITIONED BY (<partition_expr1>[, <partition_expr2>, ...])]
STORED AS ICEBERG
[LOCATION '<oss_path>']                           -- 外湖必填
[TBLPROPERTIES (
    'format_version'    = '<2|3>',                 -- 默认2
    'catalog_type'      = 'ADB',                   -- 内湖必填
    'adb_lake_bucket'   = '<bucket_name>',          -- 内湖必填
    ...
)];

子句

是否必填

说明

STORED AS ICEBERG

声明表格式为Iceberg。

PARTITIONED BY (...)

分区表达式。支持identity分区(直接使用列值)和转换函数分区(yearmonthdayhourbuckettruncate)。

LOCATION

外湖必填

指向用户自有OSS路径,格式为oss://<bucket>/<path>/。路径建议以/<database>/<table>/结尾。

TBLPROPERTIES

内湖必填

内湖模式需包含catalog_type='ADB'adb_lake_bucket='...'

TBLPROPERTIES中可配置的表属性如下:

属性

默认值

说明

catalog_type

内湖必填,取值为'ADB'

adb_lake_bucket

内湖必填,取值为AnalyticDB for MySQL分配的OSS Bucket名。

format_version

'2'

Iceberg Format Version,支持'2'(默认)和'3'。使用行级DELETE时需设置为'3'

format

'PARQUET'

数据文件的存储格式,当前仅支持'PARQUET'

metadata_location

外湖模式下,如需指向已有Iceberg数据,需填写metadata.jsonOSS路径。

外湖建表示例

-- 创建数据库
CREATE DATABASE IF NOT EXISTS lake_db;

-- 创建外湖Iceberg表
CREATE TABLE lake_db.orders (
    order_id     BIGINT       COMMENT '订单ID',
    user_id      BIGINT       COMMENT '用户ID',
    status       STRING       COMMENT '订单状态',
    total_amount DECIMAL(18, 2) COMMENT '订单金额',
    created_at   TIMESTAMP    COMMENT '下单时间'
)
COMMENT '订单主表'
PARTITIONED BY (dt DATE)
STORED AS ICEBERG
LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/orders/';
说明

外湖建表通过LOCATION指定路径,不写catalog_typeLOCATION路径建议以/<database>/<table>/结尾,例如oss://<YOUR-BUCKET>/warehouse/lake_db/orders/。无需提前创建OSS目录,引擎会自动创建目录和元数据文件。

内湖建表示例

CREATE TABLE lake_db.orders (
    order_id     BIGINT       COMMENT '订单ID',
    user_id      BIGINT       COMMENT '用户ID',
    status       STRING       COMMENT '订单状态',
    total_amount DECIMAL(18, 2) COMMENT '订单金额',
    created_at   TIMESTAMP    COMMENT '下单时间'
)
COMMENT '订单主表'
PARTITIONED BY (dt DATE)
STORED AS ICEBERG
TBLPROPERTIES (
    'catalog_type'    = 'ADB',
    'adb_lake_bucket' = 'adb-lake-cn-<region>-xxxx'
);
说明

内湖建表不写LOCATION,存储路径由AnalyticDB for MySQL自动分配。使用内湖模式前需先开通湖存储功能,adb_lake_bucket的值请参见创建数据湖表

CTAS建表(CREATE TABLE AS SELECT)

CTAS在建表的同时写入查询结果,列名和类型从SELECT推导。

CREATE TABLE lake_db.orders_copy
PARTITIONED BY (dt DATE)
STORED AS ICEBERG
LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/orders_copy/'
AS SELECT * FROM lake_db.orders;

分区管理

Iceberg的分区与传统Hive分区有本质区别:

维度

Hive分区

Iceberg分区

分区是否为列

分区列是独立列

分区可以基于现有列的转换(如day(dt)),不额外占列

写入时需感知分区

INSERT必须指定分区值

INSERT不需要显式写分区值,引擎自动路由

查询时需感知分区

必须WHERE带分区列

引擎自动进行分区裁剪

Iceberg支持identity分区(直接使用列值)和基于现有列的转换函数分区。写入时无需指定分区值,引擎根据列值自动路由。

函数

适用类型

示例

identity

任意

PARTITIONED BY (dt DATE)

year

DATE / TIMESTAMP

PARTITIONED BY (year(dt))

month

DATE / TIMESTAMP

PARTITIONED BY (month(dt))

day

DATE / TIMESTAMP

PARTITIONED BY (day(created_at))

hour

TIMESTAMP

PARTITIONED BY (hour(created_at))

bucket[N]

任意(常用于高基数字段,如 ID)

PARTITIONED BY (bucket(user_id, 16))

truncate[len]

STRING

PARTITIONED BY (truncate(email, 10))

说明

分区转换不是独立列:day(created_at)不会创建新列,IcebergManifest文件中记录转换后的分区值。分区过多会影响性能,高基数列建议使用day()month()控制分区数。

针对不同数据特征,常用的分区场景与示例如下:

  • 按时间维度分区(day,最常用):时序数据按天分区最常用,兼顾查询性能与管理成本。使用day(created_at)分区时,无需单独维护dt列,Iceberg自动从created_at提取日期做分区。

    CREATE TABLE lake_db.orders_by_day (
        order_id     BIGINT,
        user_id      BIGINT,
        status       STRING,
        total_amount DECIMAL(18, 2),
        created_at   TIMESTAMP
    )
    PARTITIONED BY (day(created_at))
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/orders_by_day/';
  • 基于低基数分类字段进行分区(identity):字段取值枚举少(如市场细分、地区),适合按原值分区。

    -- 按市场细分 mktsegment 原值分区,该字段仅少数几个枚举值,适合 identity
    CREATE TABLE lake_db.customer (
        c_custkey     BIGINT,
        c_name        STRING,
        c_acctbal     DECIMAL(15,2),
        c_mktsegment  STRING   -- 低基数:'AUTOMOBILE'、'BUILDING'、'FURNITURE' 等
    )
    PARTITIONED BY (c_mktsegment)
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/customer/';
  • 高频事件按小时分区(hour):数据写入频繁、需近实时监控时,可按小时分区。

    -- 按收货时间戳的小时分区,支持近实时监控
    CREATE TABLE lake_db.lineitem_realtime (
        l_orderkey      BIGINT,
        l_partkey       BIGINT,
        l_quantity      DECIMAL(15,2),
        l_receipttime   TIMESTAMP   -- 精确到秒的收货时间
    )
    PARTITIONED BY (hour(l_receipttime))
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/lineitem_realtime/';
  • 基于高基数字段哈希分桶(bucket):对高基数字段(如 ID)哈希分桶,可避免小文件并提升 JOIN 性能。

    -- 对高基数外键 l_partkey 哈希分 64 桶,均匀分布数据
    CREATE TABLE lake_db.lineitem (
        l_orderkey      BIGINT,
        l_partkey       BIGINT,      -- 高基数字段(约 2000 万唯一值)
        l_quantity      DECIMAL(15,2),
        l_shipdate      DATE
    )
    PARTITIONED BY (bucket(l_partkey, 64))
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/lineitem/';
  • 字符串前缀截断分区(truncate):按字符串前若干位聚合分区,适合按前缀归类的场景。

    -- 按电话号码前 3 位分区(如 '13-' 代表某运营商号段)
    CREATE TABLE lake_db.customer_by_phone (
        c_custkey     BIGINT,
        c_name        STRING,
        c_phone       STRING       -- 格式:'13-888-999-1234'
    )
    PARTITIONED BY (truncate(c_phone, 3))
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/customer_by_phone/';
  • 多级复合分区:组合多个分区方式,在分区数量与数据均匀性之间取得平衡,适用于海量数据查询场景。

    -- 用户行为日志表:按天分区 + 高基数 ID 分桶 + 区域前缀截断,三级复合分区
    CREATE TABLE lake_db.user_event_log (
        event_id      BIGINT,
        user_id       BIGINT,          -- 高基数用户 ID
        event_type    STRING,
        event_time    TIMESTAMP,       -- 精确到秒的时间戳
        country_code  STRING           -- 国家代码,如 'CN'、'US'、'DE'
    )
    PARTITIONED BY (
        day(event_time),            -- 第一级:按天分区,高效时间裁剪
        bucket(user_id, 64),        -- 第二级:对高基数 user_id 哈希分 64 桶,防小文件
        truncate(country_code, 2)   -- 第三级:截取国家代码前 2 位,区域聚合
    )
    STORED AS ICEBERG
    LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/user_event_log/';

其他DDL操作

-- 查看表的完整建表语句
SHOW CREATE TABLE lake_db.orders;

-- 查看列结构
DESCRIBE lake_db.orders;

-- 删除表
DROP TABLE IF EXISTS lake_db.orders;

-- 删除数据库(库内不能有表,否则报错)
DROP DATABASE IF EXISTS lake_db;
警告

对内湖表执行DROP TABLE会同时删除OSS上的数据文件和元数据,操作不可逆。外湖表的DROP TABLE仅删除AnalyticDB for MySQL中的表定义,OSS上的数据文件不受影响。

写入数据

INSERT INTO(追加写入)

INSERT INTO lake_db.orders
SELECT * FROM VALUES
    (1001, 501, 'paid', 299.90, TIMESTAMP '2026-06-11 10:00:00', DATE '2026-06-11'),
    (1002, 502, 'pending', 158.00, TIMESTAMP '2026-06-11 10:05:00', DATE '2026-06-11'),
    (1003, 503, 'shipped', 450.00, TIMESTAMP '2026-06-11 10:10:00', DATE '2026-06-12')
AS t(order_id, user_id, status, total_amount, created_at, dt);
说明

内核版本3.2.8之前,INSERT语法必须使用INSERT INTO ... SELECT * FROM VALUES (...) AS t(...)形式。3.2.8及以上版本支持INSERT INTO ... VALUES (...)直接写法。

INSERT OVERWRITE(覆写)

对分区表执行INSERT OVERWRITE时,Iceberg采用动态分区覆写策略——仅覆盖SELECT结果中涉及的分区,其余分区数据不受影响。

-- 仅覆盖dt='2026-06-11'分区的数据,其他分区不受影响
INSERT OVERWRITE lake_db.orders
SELECT * FROM VALUES
    (2001, 601, 'paid', 999.00, TIMESTAMP '2026-06-11 12:00:00', DATE '2026-06-11')
AS t(order_id, user_id, status, total_amount, created_at, dt);

DELETE(行级删除)

Iceberg表支持基于条件的行级删除。使用DELETE前,需在建表时将format_version设置为'3'

-- 建表时指定 format_version=3,以支持行级删除
CREATE TABLE lake_db.orders_v3 (
    order_id     BIGINT,
    user_id      BIGINT,
    status       STRING,
    total_amount DECIMAL(18, 2),
    created_at   TIMESTAMP
)
PARTITIONED BY (day(created_at))
STORED AS ICEBERG
LOCATION 'oss://<YOUR-BUCKET>/warehouse/lake_db/orders_v3/'
TBLPROPERTIES (
    'format_version' = '3'
);

-- 删除满足条件的行
DELETE FROM lake_db.orders_v3 WHERE status = 'cancelled';
说明

行级删除仅支持write.delete.mode='merge-on-read'策略:删除操作写入删除标记文件(delete file),查询时再与数据文件合并,避免立即重写数据文件。

查询数据

基本查询

SELECT * FROM lake_db.orders WHERE dt = DATE '2026-06-11';

SELECT dt, COUNT(*) AS cnt, SUM(total_amount) AS total
FROM lake_db.orders
GROUP BY dt;

分区裁剪

WHERE条件中包含分区列或分区转换函数时,引擎自动进行分区裁剪,只扫描相关分区的数据文件。

-- identity分区:直接匹配分区列
SELECT * FROM lake_db.orders WHERE dt = DATE '2026-06-11';

-- day(col)分区:匹配时间范围
SELECT * FROM lake_db.orders_by_day
WHERE created_at >= TIMESTAMP '2026-06-11 00:00:00'
  AND created_at <  TIMESTAMP '2026-06-12 00:00:00';

谓词下推

IcebergWHERE条件中的谓词下推到数据文件层面,利用Parquet文件内部的统计信息(min/max/null count)过滤不满足条件的row group,减少实际读取的数据量。

谓词下推生效条件:

  • WHERE条件中的列存在统计信息。

  • 谓词为简单比较(=<><=>=INBETWEEN)。

  • 列类型与统计信息类型匹配。

-- 谓词下推生效:order_id有min/max统计信息
SELECT * FROM lake_db.orders
WHERE order_id = 1001;

-- 范围谓词下推
SELECT * FROM lake_db.orders
WHERE total_amount BETWEEN 100 AND 500;

查询注意事项

  • 分区裁剪仅对WHERE条件中直接引用分区列或分区转换函数的场景生效。

  • 复杂谓词(如函数调用、OR条件)可能影响谓词下推效果。

  • IcebergJOIN性能取决于数据量和分区设计。

可选配置项

以下配置项可通过ConfigHint方式设置,用于调整Iceberg表的写入和查询行为。

配置名称

说明

是否需要重启

iceberg_write_max_partition

写入时每个Writer允许的最大分区数。默认值100。

iceberg_metadata_cache_enabled

元数据缓存开关。默认值true。

iceberg_manifest_cache_query_strategy

查询时Manifest缓存策略。取值:none(默认,正常使用缓存)、bypass(跳过缓存)、clear(清除缓存后读取)、reload(清除后重新缓存)。

以下为实例级配置,修改后需要重启实例生效:

配置名称

说明

默认值

ICEBERG_IO_MANIFEST_CACHE_ENABLED

Manifest文件缓存总开关。

false

ICEBERG_IO_MANIFEST_CACHE_MAX_TOTAL_BYTES

Manifest缓存最大总字节数。

104857600(100 MB)

ICEBERG_IO_MANIFEST_CACHE_EXPIRATION_INTERVAL_MS

缓存条目过期时间(毫秒)。

0(永不过期)

ICEBERG_IO_MANIFEST_CACHE_MAX_CONTENT_LENGTH

单个Manifest文件可缓存的最大字节数,超过则不缓存。

8388608(8 MB)

ICEBERG_IO_MANIFEST_CACHE_MAX_CHUNK_SIZE

缓存Manifest文件内容时的buffer最大长度。

2097152(2 MB)

使用限制

  • 字符串类型统一使用STRING,不支持VARCHARCHAR(N)

  • STORED AS ICEBERG是必需子句,缺失时不会创建Iceberg表。

  • format_version支持'2'(默认)和'3',使用行级DELETE时必须设置为'3'

  • 内核版本3.2.8之前,INSERT语法必须使用INSERT INTO t SELECT * FROM VALUES (...)形式。3.2.8及以上版本支持INSERT INTO t VALUES (...)直接写法。

  • INSERT OVERWRITE对分区表采用动态分区覆写策略,仅替换SELECT结果中涉及的分区。

  • 行级DELETE要求建表时设置format_version='3'

  • 存储模式(内湖/外湖)建表后不可变更。

  • 内湖建表必须同时指定catalog_type='ADB'adb_lake_bucket,缺一不可。外湖建表通过LOCATION指定路径,不写catalog_type

  • 分区过多会影响性能,高基数列建议使用day()month()控制分区数。