Iceberg external tables (XIHE SQL)

更新时间:
复制 MD 格式

The XIHE engine of AnalyticDB for MySQL natively supports the Apache Iceberg lake table format. You can use standard SQL to create Iceberg tables and perform data writes, queries, and schema changes.

Prerequisites

  • The cluster edition is Enterprise Edition, Basic Edition, or Data Lakehouse Edition.

  • The kernel version of the cluster is 3.2.3.0 or later.

  • An external database is created. For more information, see CREATE EXTERNAL DATABASE.

  • To use the internal lake mode (AnalyticDB-managed lake storage), you must submit a ticket to enable the lake storage feature. For more information, see Lake storage.

Background

Apache Iceberg is an open data lake table format that supports features such as ACID transactions, schema evolution, and partition transforms. Data is stored in Parquet format on OSS, and any Iceberg-compatible compute engine can directly read the data.

AnalyticDB supports two storage modes. The storage mode is determined when you create a table and cannot be changed afterward:

Dimension

Internal lake (managed lake storage)

External lake (user-owned OSS)

Storage management

Fully managed by AnalyticDB

User-managed OSS bucket

Key table creation parameter

catalog_type='ADB' + adb_lake_bucket

LOCATION 'oss://...'

Activation method

Submit a ticket to apply for activation

No additional activation required. Only authorization is needed

Applicable scenario

New projects that require simplified O&M

Existing OSS data or self-managed storage required

Create a table

Syntax

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 (
    '<key1>' = '<value1>',
    ...
)];

Clause

Required

Description

STORED AS ICEBERG

Yes

Declares the table format as Iceberg.

PARTITIONED BY (...)

No

Partition expression. Identity partitioning (using column values directly) and transform function partitioning (year, month, day, hour) are supported.

LOCATION

Required for external lake

Points to a user-owned OSS path. The format is oss://<bucket>/<path>/. The engine automatically creates the path.

TBLPROPERTIES

Required for internal lake

For the internal lake mode, you must include catalog_type='ADB' and adb_lake_bucket='...'.

The following table properties can be configured in TBLPROPERTIES:

Property

Default value

Description

catalog_type

N/A

Required for the internal lake mode. Set the value to 'ADB'.

adb_lake_bucket

N/A

Required for the internal lake mode. Set the value to the name of the OSS bucket allocated by AnalyticDB.

format_version

'2'

The Iceberg format version. Both '2' (default) and '3' are supported. Row-level DELETE requires '3'.

metadata_location

N/A

In external lake mode, if you want to point to existing Iceberg data, specify the OSS path of the metadata.json file.

identifier-fields

N/A

Identifier columns (similar to primary keys) for row-level DELETE. Format: '[col1,col2]'. Must be used with format_version='3'.

External lake table example

-- Create a database
CREATE DATABASE IF NOT EXISTS lake_db;

-- Create an external lake Iceberg table
CREATE TABLE lake_db.orders (
    order_id     BIGINT       COMMENT 'Order ID',
    user_id      BIGINT       COMMENT 'User ID',
    status       STRING       COMMENT 'Order status',
    total_amount DECIMAL(18, 2) COMMENT 'Order amount',
    created_at   TIMESTAMP    COMMENT 'Order time'
)
COMMENT 'Orders table'
PARTITIONED BY (dt DATE)
STORED AS ICEBERG
LOCATION 'oss://<your-bucket>/warehouse/lake_db/orders/';
Note

In external lake mode, the engine automatically creates directories and metadata files in the specified OSS path. You do not need to create the OSS directory in advance.

Internal lake table example

CREATE TABLE lake_db.orders (
    order_id     BIGINT       COMMENT 'Order ID',
    user_id      BIGINT       COMMENT 'User ID',
    status       STRING       COMMENT 'Order status',
    total_amount DECIMAL(18, 2) COMMENT 'Order amount',
    created_at   TIMESTAMP    COMMENT 'Order time'
)
COMMENT 'Orders table'
PARTITIONED BY (dt DATE)
STORED AS ICEBERG
TBLPROPERTIES (
    'catalog_type'    = 'ADB',
    'adb_lake_bucket' = 'adb-lake-cn-<region>-xxxx'
);
Note

When you create an internal lake table, you do not need to specify LOCATION. The storage path is automatically allocated by AnalyticDB. You can find the value of adb_lake_bucket on the lake storage page in the console.

CTAS (CREATE TABLE AS SELECT)

CTAS creates a table and writes the query results at the same time. The column names and types are inferred from the SELECT statement.

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;

Partition transform functions

In addition to identity partitioning (using column values directly), Iceberg supports transform function partitioning based on existing columns. You do not need to specify partition values during writes. The engine automatically routes data based on column values.

Function

Applicable types

Example

identity

Any

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))

When you use day(created_at) for partitioning, you do not need to maintain a separate dt column. Iceberg automatically extracts the date from created_at for partitioning:

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

Other DDL operations

-- View the complete CREATE TABLE statement
SHOW CREATE TABLE lake_db.orders;

-- View the column structure
DESCRIBE lake_db.orders;

-- Drop a table
DROP TABLE IF EXISTS lake_db.orders;
Warning

Executing DROP TABLE on an internal lake table permanently deletes both the data files and metadata on OSS. This operation is irreversible. The behavior of DROP TABLE on an external lake table depends on the ownership settings of the storage path.

Write data

INSERT INTO (append)

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);
Important

The INSERT syntax must use the INSERT INTO t SELECT * FROM VALUES (...) form. Using INSERT INTO t VALUES (...) directly causes an error.

INSERT OVERWRITE (overwrite)

On a partitioned table, INSERT OVERWRITE uses a dynamic partition overwrite strategy: only partitions present in the SELECT result are replaced, leaving other partitions unchanged.

-- Only overwrite the data in the dt='2026-06-11' partition. Other partitions are not affected.
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 (row-level delete)

Iceberg tables support row-level deletion. To use DELETE, the following requirements must be met:

  • format_version is set to '3' when the table is created.

  • identifier-fields (identifier columns, similar to primary keys) is specified in TBLPROPERTIES when the table is created.

-- Create a table: specify format_version=3 and identifier-fields
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',
    'identifier-fields' = '[order_id]'
);

-- Delete rows that meet the condition
DELETE FROM lake_db.orders_v3 WHERE status = 'cancelled';

Query data

Basic queries

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;

Partition pruning

When the WHERE clause references partition columns or transform functions, the engine automatically prunes irrelevant partitions and scans only matching data files.

-- Identity partitioning: match the partition column directly
SELECT * FROM lake_db.orders WHERE dt = DATE '2026-06-11';

-- day(col) partitioning: match the time range
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';

Predicate pushdown

Iceberg pushes WHERE predicates down to the data file level and uses Parquet file statistics (min/max/null count) to skip row groups that do not match. This reduces the amount of data read. Supported predicates include equality (=), range (<, >, BETWEEN), and IN.

Modify table schema

Add columns

ALTER TABLE lake_db.orders ADD COLUMNS (
    region STRING COMMENT 'Order region'
);

-- Verify
DESCRIBE lake_db.orders;

The new column is appended to the end of the column list. The value of the new column is NULL for existing rows.

Note

New columns must be nullable. Adding NOT NULL columns is not supported. Schema changes only modify the schema definition in metadata.json and do not rewrite existing data files.

Type promotion

Safe upward type promotion is supported. For example, INT to BIGINT:

ALTER TABLE lake_db.orders CHANGE COLUMN order_id order_id BIGINT;

Configuration parameters

You can set these parameters by using Config or Hint to adjust the write and query behavior of Iceberg tables.

Parameter

Description

Restart required

iceberg_write_max_partition

The maximum number of partitions allowed per writer during writes. Default value: 100.

No

iceberg_metadata_cache_enabled

Enables or disables the metadata cache. Default value: true.

No

iceberg_manifest_cache_query_strategy

The manifest cache strategy for queries. Valid values: none (default, use the cache normally), bypass (skip the cache), clear (clear the cache before reading), and reload (clear the cache and re-cache).

No

The following instance-level parameters require an instance restart to take effect:

Parameter

Description

Default value

ICEBERG_IO_MANIFEST_CACHE_ENABLED

The master switch for manifest file caching.

false

ICEBERG_IO_MANIFEST_CACHE_MAX_TOTAL_BYTES

The maximum total bytes for the manifest cache.

104857600 (100 MB)

ICEBERG_IO_MANIFEST_CACHE_EXPIRATION_INTERVAL_MS

The expiration time for cache entries, in milliseconds.

0 (never expires)

ICEBERG_IO_MANIFEST_CACHE_MAX_CONTENT_LENGTH

The maximum bytes of a single manifest file that can be cached. Files exceeding this limit are not cached.

8388608 (8 MB)

Limits

  • Use STRING for string types. VARCHAR and CHAR(N) are not supported.

  • STORED AS ICEBERG is a required clause. The table is not created as an Iceberg table if this clause is missing.

  • Both format_version='2' (default) and '3' are supported. Row-level DELETE requires '3'.

  • The INSERT syntax must use the INSERT INTO t SELECT * FROM VALUES (...) form. The INSERT INTO t VALUES (...) form is not supported.

  • INSERT OVERWRITE uses a dynamic partition overwrite strategy for partitioned tables. Only the partitions involved in the SELECT result are replaced.

  • Row-level DELETE requires format_version='3' and identifier-fields.

  • Columns added by ALTER TABLE ADD COLUMNS must be nullable.

  • The storage mode (internal lake or external lake) cannot be changed after the table is created.

  • Too many partitions can degrade performance. For high-cardinality columns, use day() or month() to limit the partition count.