Iceberg外表(XIHE SQL)

更新时间:
复制 MD 格式

AnalyticDB for MySQLXIHE引擎原生支持Apache Iceberg湖表格式。您可以通过标准SQL创建Iceberg表,并执行数据写入和查询操作。本文介绍Iceberg外表(XIHE SQL)的方法。

前提条件

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

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

  • 已创建外部数据库。创建方法请参见CREATE EXTERNAL DATABASE

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

背景信息

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

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

维度

内湖(托管湖存储)

外湖(用户自有OSS)

存储管理

AnalyticDB全托管

用户自管理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分区(直接使用列值)和转换函数分区(yearmonthdayhour)。

LOCATION

外湖必填

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

TBLPROPERTIES

内湖必填

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

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

属性

默认值

说明

catalog_type

内湖必填,取值为'ADB'

adb_lake_bucket

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

format_version

'2'

Iceberg Format Version,支持'2'(默认)和'3'

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/

内湖建表示例

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自动分配。使用内湖模式前需先开通湖存储功能,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))

说明

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

使用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/';

其他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中的表定义,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 FROM lake_db.orders WHERE status = 'cancelled';

查询数据

基本查询

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)

使用限制

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

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

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

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

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

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

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