Amazon Redshift が Apache Iceberg v3 テーブルをサポートしたので rg.large で試してみた
はじめに
2026年8月31日、Amazon Redshift がデータレイク上の Apache Iceberg v3 テーブルの読み取りと書き込みに対応しました。
v3 の追加要素のうち、今回 Redshift がサポートしたのはデフォルト列値・行リネージ・削除ベクターの3つです。v3 テーブルの読み書きに対応する実行基盤は、RG インスタンスタイプまたは Redshift Serverless です。
v3 テーブルには対象外の機能もあります。
- You can't read or write complex types (struct, list, map, variant) in Iceberg v3 tables.
- You can't use Iceberg v3 tables that contain equality deletes.
- You can't create materialized views on Iceberg v3 tables.
v3 テーブルでは複合型の列を読み書きできず、マテリアライズドビューも作成できません。
You can query Apache Iceberg tables cataloged in the AWS Glue Data Catalog with Amazon Redshift. RG instance types and Redshift Serverless use their own compute to process data lake queries, while RA3 instance types use Redshift Spectrum.
When you access Iceberg tables from an RG cluster or a Redshift Serverless workgroup, data lake queries run on the cluster's or workgroup's own compute resources, so there is no separate charge for data lake queries. When you access Iceberg tables from a DC2 or RA3 cluster, you are charged Redshift Spectrum pricing.
RG と Serverless は、データレイククエリを自身のコンピュートで実行します。
本記事では、RG インスタンスタイプの rg.large のクラスターから、Glue Data Catalog 経由で Iceberg v3 テーブルを作成します。クラスター・S3・Glue・IAM の初期構成を済ませたあと、テーブル操作はすべて SQL で実行しました。
検証内容
v3 の追加要素が SQL からどう見えるかを、v2 との比較を通して確認しました。
検証環境
インスタンスタイプが Iceberg クエリの実行基盤に関わるため、使用したクラスターの構成を示します。
| 項目 | 値 |
|---|---|
| NodeType | rg.large |
| NumberOfNodes | 2 |
| ClusterVersion | 1.0 |
| AvailabilityZone | ap-northeast-1c |
| ClusterStatus | available |
データベース名は dev です。クラスターに適用した IAM ロールの ApplyStatus は in-sync でした。
Iceberg テーブルを操作する前に、Glue Data Catalog のデータベースを指す外部スキーマを作成しました。
CREATE EXTERNAL SCHEMA iceberg FROM DATA CATALOG DATABASE 'devio_iceberg_v3' IAM_ROLE 'arn:aws:iam::123456789012:role/devio-iceberg-v3-redshift';
ロール ARN はマスク済みの値に置き換えています。
デフォルト列値
format-version を 3 に指定した v3 テーブルを作成しました。DEFAULT を定義した status 列を省略して3行を INSERT しました。
CREATE TABLE iceberg.orders_v3 (order_id int, status varchar DEFAULT 'pending', amount int)
USING ICEBERG
LOCATION 's3://amzn-s3-demo-bucket/orders_v3/'
TABLE PROPERTIES ('format-version'='3');
INSERT INTO iceberg.orders_v3 (order_id, amount) VALUES (1,100),(2,200),(3,300);
バケット名は実際の値をマスクしています。
SELECT した結果は次のとおりです。
| order_id | status | amount |
|---|---|---|
| 1 | pending | 100 |
| 2 | pending | 200 |
| 3 | pending | 300 |
INSERT で指定しなかった status 列には、定義したデフォルト値 pending が入りました。
次に、既存行があるテーブルへ DEFAULT 付きの region 列を追加しました。
ALTER TABLE iceberg.orders_v3 ADD COLUMN region varchar DEFAULT 'ap-northeast-1';
SELECT order_id,status,amount,region FROM iceberg.orders_v3 ORDER BY order_id;
| order_id | status | amount | region |
|---|---|---|---|
| 1 | pending | 100 | ap-northeast-1 |
| 2 | pending | 200 | ap-northeast-1 |
| 3 | pending | 300 | ap-northeast-1 |
追加前から入っていた3行が、UPDATE なしで region の値を返しました。
v2 との差
DEFAULT を使わない構成で v2 テーブルを作成し、列追加だけ v3 と同じ操作を実行しました。テーブルプロパティで format-version を指定していないので、v2 として作成されます。
CREATE TABLE iceberg.orders_v2 (order_id int, status varchar, amount int)
USING ICEBERG
LOCATION 's3://amzn-s3-demo-bucket/orders_v2/';
INSERT INTO iceberg.orders_v2 VALUES (1,'pending',100),(2,'pending',200),(3,'pending',300);
このテーブルに v3 と同じ region 列を DEFAULT 付きで追加すると、ステートメントは失敗しました。
ALTER TABLE iceberg.orders_v2 ADD COLUMN region varchar DEFAULT 'ap-northeast-1';
ERROR: Columns constraints and attributes are not supported for an "iceberg" table.
Hint: Default values are only supported with Iceberg version "3".
DEFAULT を外すと列の追加は成功しました。
ALTER TABLE iceberg.orders_v2 ADD COLUMN region varchar;
SELECT order_id,status,amount,region FROM iceberg.orders_v2 ORDER BY order_id;
| order_id | status | amount | region |
|---|---|---|---|
| 1 | pending | 100 | NULL |
| 2 | pending | 200 | NULL |
| 3 | pending | 300 | NULL |
v2 のままでは、列を追加したあとに既存行を埋める UPDATE が必要です。
行リネージ
v3 テーブルでは、行ごとの識別子 _row_id と最終更新シーケンス番号 _last_updated_sequence_number を疑似列として参照できます。
更新前の値を確認してから、order_id が 2 の行だけを UPDATE しました。
SELECT _row_id,_last_updated_sequence_number,order_id,status FROM iceberg.orders_v3 ORDER BY order_id;
UPDATE iceberg.orders_v3 SET status='shipped' WHERE order_id=2;
| order_id | 更新前 _row_id | 更新前 _last_updated_sequence_number | 更新後 _row_id | 更新後 _last_updated_sequence_number | 更新後 status |
|---|---|---|---|---|---|
| 1 | 2 | 1 | 2 | 1 | pending |
| 2 | 1 | 1 | 1 | 3 | shipped |
| 3 | 0 | 1 | 0 | 1 | pending |
order_id が 2 の行だけ、シーケンス番号が 1 から 3 に変わり、_row_id は 1 のままでした。これにより、UPDATE をまたいで同じ行を追跡しながら、更新された行だけを見分けられます。
UPDATE 後に実測したシーケンス番号 3 を条件に指定して、増分抽出を実行しました。
SELECT _row_id,_last_updated_sequence_number,order_id,status FROM iceberg.orders_v3 WHERE _last_updated_sequence_number >= 3 ORDER BY order_id;
| _row_id | _last_updated_sequence_number | order_id | status |
|---|---|---|---|
| 1 | 3 | 2 | shipped |
返ってきたのは、更新した1行だけでした。更新日時列やフラグ列をテーブルに持たせなくても、更新分を抽出できます。
撤去
rg.large のクラスターは、削除するまで課金が続きます。テーブルと外部スキーマを SQL で削除しました。
DROP TABLE IF EXISTS iceberg.orders_v3;
DROP TABLE IF EXISTS iceberg.orders_v2;
DROP SCHEMA IF EXISTS iceberg;
この時点で S3 には 39 件のオブジェクトが残っていました。Iceberg テーブルの削除はカタログ側のメタデータ操作なので、データファイルは S3 に残ります。残りは AWS CLI で削除しました。
aws s3 rm s3://<バケット名> --recursive
aws s3api delete-bucket --bucket <バケット名>
aws glue delete-database --name devio_iceberg_v3
aws redshift delete-cluster --cluster-identifier <クラスター識別子> --skip-final-cluster-snapshot
aws redshift wait cluster-deleted --cluster-identifier <クラスター識別子>
aws iam delete-role-policy --role-name <ロール名> --policy-name <ポリシー名>
aws iam delete-role --role-name <ロール名>
クラスターの削除は非同期なので、完了を待ってから IAM ロールを削除しました。
まとめ
rg.large のクラスターから Iceberg v3 テーブルを作成し、列追加時に既存行へ返るデフォルト値と、更新行の抽出まで SQL だけで実行できました。
更新日時列やフラグ列を自分で持たせて差分を取っている ETL は行リネージの疑似列に、列を追加するたびに既存データを一括 UPDATE している運用は DEFAULT 付きの列追加に寄せられます。更新の検知に自作の列は要らず、列追加のために既存行を書き換える必要もありません。
RG のクラスターはデータレイククエリを自身のコンピュートで実行するため、Iceberg テーブルへのクエリに Spectrum の課金が別途発生しません。Iceberg v3 を使うときは、複合型やマテリアライズドビューなどの制約を確認したうえで、テーブルプロパティに format-version を指定してお試しください。







