Iceberg外表(XIHE SQL)
AnalyticDB for MySQL的XIHE引擎原生支持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 |
建表关键参数 |
|
|
开通方式 | 需提交工单申请开通 | 同账号下无需额外操作 |
适用场景 | 新项目,希望简化运维 | 已有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>', -- 内湖必填
...
)];子句 | 是否必填 | 说明 |
| 是 | 声明表格式为Iceberg。 |
| 否 | 分区表达式。支持identity分区(直接使用列值)和转换函数分区( |
| 外湖必填 | 指向用户自有OSS路径,格式为 |
| 内湖必填 | 内湖模式需包含 |
TBLPROPERTIES中可配置的表属性如下:
属性 | 默认值 | 说明 |
| 无 | 内湖必填,取值为 |
| 无 | 内湖必填,取值为AnalyticDB分配的OSS Bucket名。 |
|
| Iceberg Format Version,支持 |
| 无 | 外湖模式下,如需指向已有Iceberg数据,需填写 |
外湖建表示例
-- 创建数据库
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_type。LOCATION路径建议以/<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分区 |
分区是否为列 | 分区列是独立列 | 分区可以基于现有列的转换(如 |
写入时需感知分区 | INSERT必须指定分区值 | INSERT不需要显式写分区值,引擎自动路由 |
查询时需感知分区 | 必须WHERE带分区列 | 引擎自动进行分区裁剪 |
Iceberg支持identity分区(直接使用列值)和基于现有列的转换函数分区。写入时无需指定分区值,引擎根据列值自动路由。
函数 | 适用类型 | 示例 |
identity | 任意 |
|
| DATE / TIMESTAMP |
|
| DATE / TIMESTAMP |
|
| DATE / TIMESTAMP |
|
| TIMESTAMP |
|
分区转换不是独立列:day(created_at)不会创建新列,Iceberg在Manifest文件中记录转换后的分区值。分区过多会影响性能,高基数列建议使用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';谓词下推
Iceberg将WHERE条件中的谓词下推到数据文件层面,利用Parquet文件内部的统计信息(min/max/null count)过滤不满足条件的row group,减少实际读取的数据量。
谓词下推生效条件:
WHERE条件中的列存在统计信息。
谓词为简单比较(
=、<、>、<=、>=、IN、BETWEEN)。列类型与统计信息类型匹配。
-- 谓词下推生效: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条件)可能影响谓词下推效果。
跨Iceberg表JOIN性能取决于数据量和分区设计。
可选配置项
以下配置项可通过Config或Hint方式设置,用于调整Iceberg表的写入和查询行为。
配置名称 | 说明 | 是否需要重启 |
| 写入时每个Writer允许的最大分区数。默认值100。 | 否 |
| 元数据缓存开关。默认值true。 | 否 |
| 查询时Manifest缓存策略。取值: | 否 |
以下为实例级配置,修改后需要重启实例生效:
配置名称 | 说明 | 默认值 |
| Manifest文件缓存总开关。 | false |
| Manifest缓存最大总字节数。 | 104857600(100 MB) |
| 缓存条目过期时间(毫秒)。 | 0(永不过期) |
| 单个Manifest文件可缓存的最大字节数,超过则不缓存。 | 8388608(8 MB) |
使用限制
字符串类型统一使用
STRING,不支持VARCHAR或CHAR(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()控制分区数。