Iceberg外表(XIHE SQL)
云原生数据仓库 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 |
|
建表关键参数 |
|
|
|
开通方式 |
需提交工单申请开通 |
同账号下无需额外操作 |
|
适用场景 |
新项目,希望简化运维 |
已有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 for MySQL分配的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/。无需提前创建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分区 |
|
分区是否为列 |
分区列是独立列 |
分区可以基于现有列的转换(如 |
|
写入时需感知分区 |
INSERT必须指定分区值 |
INSERT不需要显式写分区值,引擎自动路由 |
|
查询时需感知分区 |
必须WHERE带分区列 |
引擎自动进行分区裁剪 |
Iceberg支持identity分区(直接使用列值)和基于现有列的转换函数分区。写入时无需指定分区值,引擎根据列值自动路由。
|
函数 |
适用类型 |
示例 |
|
identity |
任意 |
|
|
|
DATE / TIMESTAMP |
|
|
|
DATE / TIMESTAMP |
|
|
|
DATE / TIMESTAMP |
|
|
|
TIMESTAMP |
|
|
|
任意(常用于高基数字段,如 ID) |
|
|
|
STRING |
|
分区转换不是独立列:day(created_at)不会创建新列,Iceberg在Manifest文件中记录转换后的分区值。分区过多会影响性能,高基数列建议使用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';
谓词下推
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) |
|
|
缓存Manifest文件内容时的buffer最大长度。 |
2097152(2 MB) |
使用限制
-
字符串类型统一使用
STRING,不支持VARCHAR或CHAR(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()控制分区数。