こんにちは。クロスインダストリー第1本部 松田です。
2024年12月に Amazon S3 Tables が GA となりました。S3 Tables は Apache Iceberg テーブルのフルマネージドストレージで、コンパクション・スナップショット管理・不要ファイル削除が自動化されます。
既存の Iceberg 環境を運用しているチームとしては「移行するとどれだけメリットがあるのか?」が気になるところです。 今回は、既存の Iceberg パイプラインを維持したまま S3 Tables を並行導入し、性能・コスト・運用面のメリットを実測で検証しました。
本ブログで得られる内容
- S3 Iceberg から S3 Tables への移行方針と選定基準
- 並行運用アーキテクチャの実装上の注意ポイント(DDL構文、IAM設計)
- 性能・料金・自動メンテナンスの実測データ
- MCP サーバーによる S3 Tables 操作の開発体験
移行方針の比較 — 2つのアプローチ
AWS Prescriptive Guidance では、Iceberg の移行オプションとしてインプレース移行(snapshot / migrate)とフルデータ移行(CTAS / INSERT)が紹介されています。ただし、S3 Tables への移行に使えるのはフルデータ移行のみです。インプレース移行は S3 Tables への移行には対応していません。
フルデータ移行をベースに、本記事では2つのアプローチを比較します。
フルデータ移行(一括切り替え)
CTAS や Spark で既存データを S3 Tables に完全に書き換える方法です。パーティション再設計やファイルサイズ最適化が可能ですが、データ量に比例した処理時間とコストが発生します。小規模テーブルや新規構築に向いています。
並行運用(デュアルライト)— 本記事で採用
既存 Iceberg パイプラインを維持したまま、同一ソースから S3 Tables にも並行してデータマートを作成する方法です。既存環境に一切の変更を加えないため、問題発生時は S3 Tables 系統を停止するだけでロールバックできます。
大規模テーブルや、移行後のデータ検証が重要なシステム、稼働停止が許されない環境に適していると思います。
| 観点 | フルデータ移行 | 並行運用 |
|---|---|---|
| データ書き換え | 全量 | 全量(並行系統のみ) |
| 既存環境への影響 | 中(カットオーバー) | 低 |
| ロールバック容易性 | 低 | 高 |
| 性能比較の可否 | 移行後のみ | 移行前に実施可能 |
本記事では、既存のパイプラインを稼働させたまま S3 Tables の検証を行うというユースケースに基づき、並行運用を採用しました。
並行運用の実装上の注意ポイント
今回の環境はメダリオンアーキテクチャ(Raw → Transformed → Analysis)で構成されています。並行運用では、Transformed 層(共通ソース)から分岐し、Analysis 層を Iceberg / S3 Tables の両系統で作成します。
┌─ Analysis(既存 Iceberg)
Raw → Transformed ─┤
└─ Analysis(S3 Tables)← 追加
既存の Iceberg 系統は一切変更せず、S3 Tables 系統を追加するだけ、という設計方針です。しかし、実装してみると Athena × S3 Tables の組み合わせにはドキュメントだけでは読み取りにくい注意ポイントがいくつかありました。
SQL の差分は小さい、しかし注意点は別のところに
まず、CTAS の SQL 差分を示します。
-- 既存 Iceberg CREATE TABLE IF NOT EXISTS my_table WITH ( table_type = 'ICEBERG', format = 'PARQUET', location = 's3://my-bucket/path/', is_external = false, partitioning = ARRAY['category', 'year', 'month'] ) AS SELECT ... -- S3 Tables CREATE TABLE IF NOT EXISTS s3tables_catalog."my_namespace".my_table WITH ( format = 'PARQUET', partitioning = ARRAY['category', 'year', 'month'] ) AS SELECT ...
table_type、location、is_external が不要になり、SELECT 文やパーティション定義は同一です。SQL の移行コスト自体は低いのですが、問題は SQL の外にありました。
DDL ごとに異なるテーブル指定方法
Athena では DDL の種類によって S3 Tables テーブルの指定方法が異なります。 これはドキュメントを読むだけでは気づきにくいポイントです。
| DDL | テーブルの指定方法 | 例 |
|---|---|---|
| CTAS | SQL 文にフルパスで記述(カタログ・namespace・テーブル名) | s3tables_catalog."my_namespace".my_table |
| DROP TABLE | SQL 文にはテーブル名のみ。カタログと namespace は queryExecutionContext の Catalog・Database で指定 | SQL: my_tablequeryExecutionContext: {"Catalog": "s3tables_catalog", "Database": "my_namespace"} |
| CREATE VIEW | S3 Tables 上には作成不可 | — |
CTAS ではフルパスで指定できるのに、DROP TABLE ではフルパスが使えず queryExecutionContext でカタログと namespace を分離して渡す必要があります。これは Athena SQL パーサーの制約によるもので、DDL ごとに queryExecutionContext を切り替える対応をしました。
-- CTAS: SQL 内にフルパスで指定 CREATE TABLE IF NOT EXISTS s3tables_catalog."my_namespace".my_table WITH (...) AS SELECT ... -- queryExecutionContext はデフォルトカタログのまま -- DROP TABLE: SQL にはテーブル名のみ DROP TABLE my_table -- queryExecutionContext で Catalog と Database を指定 -- { "Catalog": "s3tables_catalog", "Database": "my_namespace" }
また、S3 Tables 上には CREATE VIEW が作成できません。 既存 Iceberg ではビュー経由でテーブルを参照していましたが、S3 Tables 系統ではビューを作成せず直接テーブル参照とする設計に変更しています。
テーブルバケット名のハイフン問題
S3 Tables のバケット命名規則ではハイフンのみ許可(アンダースコア不可)です。一方、Athena SQL パーサーはハイフンをマイナス演算子として解釈します。
既存 Iceberg では queryExecutionContext でデータベース名を渡すため SQL パーサーを通りませんでした。
しかし S3 Tables はクロスカタログ参照のため SQL 文字列内にカタログパスを記述する必要があり、そのままではパースエラーになります。
解決策として、Athena データソースとしてハイフンを含まない名前で登録しました。
aws athena create-data-catalog \
# ★ --name にはハイフンを含まない名前を指定する。
# この名前が Athena SQL 内でのカタログ参照名になる。
--name "s3tables_analysis" \
--type GLUE \
# ★ catalog-id にはテーブルバケットの実際のパスを指定する。
# --name とは異なる名前空間であることに注意。
--parameters '{"catalog-id": "<アカウントID>:s3tablescatalog/<テーブルバケット名>"}'
これにより、SQL 内では s3tables_analysis というハイフンを含まない名前で参照できます。
IAM ポリシー設計 — カタログ参照名 ≠ Glue カタログパス
S3 Tables を Athena から参照するには、Glue のフェデレーテッドカタログという仕組みを経由します。全体の階層構造は以下のとおりです。
- 親カタログ
s3tablescatalog: S3 Tables 用に予約された固定名のカタログ。アカウントに1つ存在し、名前は変更できない - 子カタログ: テーブルバケットごとに自動作成される。この配下に Namespace やテーブルがぶら下がる
- カタログ参照名:
aws athena create-data-catalogで子カタログに紐づけるエイリアス。Athena SQL 内ではこの名前で参照する
ここで注意が必要なのは、Athena SQL で使うカタログ参照名と、IAM ポリシーで指定する Glue カタログパスが異なる点です。
- Athena SQL:
s3tables_analysis(カタログ参照名) - IAM ポリシーの Resource:
s3tablescatalog/<テーブルバケット名>(Glue カタログの実際の階層パス)
この2つの名前空間を混同すると AccessDeniedException が発生します。IAM ポリシーに必要な ARN は以下の4階層です。
arn:aws:glue:<region>:<account>:catalog/s3tablescatalog ← 親カタログ(必須) arn:aws:glue:<region>:<account>:catalog/s3tablescatalog/<tableBucketName> ← 子カタログ arn:aws:glue:<region>:<account>:database/s3tablescatalog/<tableBucketName>/* ← DB arn:aws:glue:<region>:<account>:table/s3tablescatalog/<tableBucketName>/*/* ← テーブル
親カタログ s3tablescatalog にも権限が必要です。 これを省略すると Athena がカタログ階層を解決できず、テーブルが見つからないエラーになります。
その他、構築時のハマりポイント
Glue Catalog 定義時には DataLakeAccess プロパティの設定が必須
CDK で AWS::Glue::Catalog を定義する際、CfnCatalog が未実装のため cdk.CfnResource で直接定義する必要があります。
さらに DataLakeAccess を明示的に false に設定しないと、CloudFormation のリソースハンドラが内部エラーを返します。
(これは DataLakeAccessProperties が未指定の場合のみ発生するようです。)
DataLakeAccess は Lake Formation によるアクセス制御を有効にするかどうかのプロパティで、S3 Tables のフェデレーテッドカタログでは false で問題ありません。
new cdk.CfnResource(this, 'FederatedCatalog', { type: 'AWS::Glue::Catalog', properties: { Name: '<カタログ名>', FederatedCatalog: { Identifier: 'arn:aws:s3tables:<region>:<account>:bucket/*', ConnectionName: 'aws:s3tables', }, // ★ DataLakeAccess を明示的に false にしないと内部エラーになる CatalogProperties: { DataLakeAccessProperties: { DataLakeAccess: false, }, }, }, });
Lake Formation 権限の明示的な付与
S3 Tables のフェデレーテッドカタログに対して、Athena クエリを実行するロールに Lake Formation 権限を明示的に付与する必要があります。具体的には、Lake Formation コンソールまたは API で、対象ロールにフェデレーテッドカタログ配下の Database / Table への SELECT 等の権限を付与します。
CreateDatabaseDefaultPermissions を空にしている環境では、IAM ポリシーだけでは権限が通らないため、この手順を忘れると Insufficient Lake Formation permission(s) エラーが発生します。
aws lakeformation grant-permissions \
--principal '{"DataLakePrincipalIdentifier":"arn:aws:iam::<account>:role/<ロール名>"}' \
--resource '{"Database":{"CatalogId":"<account>","Name":"s3tablescatalog/<テーブルバケット名>/<Namespace>"}}' \
--permissions '["ALL"]'
テーブルバケット削除後の再作成制限
テーブルバケットを削除すると、同名での再作成ができない期間が10〜15分ほど発生します。開発中にスタックの削除・再デプロイを繰り返す場合は注意が必要です。
性能・コスト・運用の実測結果
構築ができたあと、並行運用で作成した Iceberg / S3 Tables 双方のテーブルを使い、性能・コスト・運用の3つの観点で比較しました。
性能比較 — 小規模テーブルでは差が出ない
対象の検証用テーブルは約80カラム・285万行・約129MBのデータマートです。COUNT と全カラムスキャンの集計クエリで比較しました。
| 項目 | Iceberg | S3 Tables |
|---|---|---|
| ファイル数 | 13 | 13 |
| 合計サイズ | ~129 MB | ~129 MB |
| COUNT 実行時間 | 2,525 ms | 2,439 ms |
| フルスキャン集計(平均) | 1,671 ms | 3,000 ms |
有意な性能差は確認されませんでした。S3 Tables の自動コンパクションは、大量の小さなファイルが存在する状態(ファイル断片化)で効果を発揮します。13ファイル・平均10MB という既に最適な状態では、最適化の余地がありません。
AWS 公式ベンチマークでは 3TB の TPC-DS データセットで最大3.2倍の高速化が報告されています。本検証との規模差(129MB vs 3TB)を考えると、この結果は妥当です。
S3 Tables の真の価値は「性能を向上させる」ことではなく「性能劣化を自動的に防止する」点にあります。 これを確認するために、次に断片化テストを実施しました。
断片化テスト — 自動コンパクションの効果を検証
S3 Tables の自動コンパクションが実際に機能するかを確認するため、意図的にファイル断片化を発生させるテストを実施しました。
テスト手順
S3 Tables のテスト用テーブルに対して、パーティション単位で繰り返し INSERT を実行してファイルを断片化させました。
自動コンパクションが発生するためには、パーティション単位で一定のファイル数を超える必要があります。1パーティションに集中して INSERT を追加し596ファイルまで断片化させたところ発動しました。
断片化直後の状態
| 項目 | 値 |
|---|---|
| テーブル全体ファイル数 | 692 |
| 断片化パーティション ファイル数 | 596 |
| 断片化パーティション サイズ | ~118 MB |
コンパクション目標(targetFileSizeMB: 512)に対して、断片化パーティションは平均ファイルサイズが約0.2MBと大幅に小さい状態です。
S3 Tables メンテナンスジョブの経過
S3 Tables では3種類のメンテナンスジョブがテーブル作成時にデフォルトで有効化されます。
| メンテナンス種別 | 役割 | デフォルト設定 |
|---|---|---|
| コンパクション | 小さなファイルを統合 | strategy: auto targetFileSizeMB: 512 |
| スナップショット管理 | 古いスナップショットを削除 | minSnapshotsToKeep: 1 maxSnapshotAgeHours: 120 |
| 不要ファイル削除 | 参照されなくなったファイルを削除 | — |
なお、上記は即時実行ではなく、AWS 側が管理するスケジュールで非同期に実行されます。
自動コンパクション後の状態
約2~3時間後に replace 操作が記録され、コンパクションが実行されました。
| 項目 | コンパクション前 | コンパクション後 |
|---|---|---|
| テーブル全体ファイル数 | 692 | 97 |
| 断片化パーティション ファイル数 | 596 | 1 |
| 断片化パーティション サイズ | ~118 MB | ~99 MB(約16%削減) |
参考: 従来 Iceberg で同等の運用を実現する場合
S3 Tables が自動で行うメンテナンスを、従来 Iceberg で実現する場合を比較します。
| メンテナンス | S3 Tables | 従来 Iceberg |
|---|---|---|
| コンパクション | デフォルト有効 | Athena OPTIMIZE を定期実行 |
| スナップショット管理・不要ファイル削除 | デフォルト有効 | Athena VACUUM を定期実行 |
| テーブル追加時 | 設定不要 | メンテナンスジョブの対象にテーブルを追加する必要あり |
テーブル数が増えるほど、自前メンテナンスの運用・監視コストは無視できなくなります。 S3 Tables はそのコストをゼロにできる点が、最も大きな運用メリットです。
料金比較 — コスト優位ではないが、運用品質に差がある
東京リージョンにおける S3 Tables と S3 Standard の主要な単価差を示します。
| 項目 | S3 Standard | S3 Tables Standard | 差額 |
|---|---|---|---|
| ストレージ(最初の50TB) | $0.025/GB-月 | $0.0288/GB-月 | +15.2% |
| リクエスト(PUT/GET) | 同額 | 同額 | — |
S3 Tables のストレージ単価は約15%高く、1TB規模のテーブルでは月額約$4の差額が発生します。料金面での優位性はありません。
ただし、S3 Tables では Intelligent-Tiering をテーブル単位で有効化できます。従来の S3 ではバケット単位・プレフィックス単位で管理する必要がありましたが、S3 Tables ではテーブルやテーブルバケットという論理単位で設定できるため、管理が直感的になっています。
S3 Tables の真の価値は、ストレージコストの削減ではなく、メンテナンスの完全自動化による運用品質の向上にあります。 コンパクションを月1回スケジュール実行する場合、実行間の最大30日間は断片化が進行し、クエリ性能が徐々に劣化します。S3 Tables の自動コンパクションは継続的に動作するため、常に最適な状態を維持できます。
MCP サーバーによる開発体験の変化
S3 Tables への移行で得られるもう一つのメリットが、MCP(Model Context Protocol)サーバーとの統合です。AWS が公式に提供する awslabs.s3-tables-mcp-server を使うことで、AI アシスタントから自然言語で S3 Tables を操作できます。
セットアップは MCP 設定ファイルに以下を追加するだけです。
{ "mcpServers": { "awslabs.s3-tables-mcp-server": { "command": "uvx", "args": ["awslabs.s3-tables-mcp-server@latest"], "env": { "AWS_PROFILE": "<プロファイル名>", "AWS_REGION": "ap-northeast-1" } } } }
Kiro、Claude Codeなど、MCP に対応した AI アシスタントで利用可能です。以下は Claude Code での実演です。
実演1: テーブル探索からデータ集計まで
Q: 「S3 Tables のテーブルバケットにどんなテーブルがあるか教えて」
AI アシスタントは list_table_buckets → list_tables を自動で呼び出し、テーブル一覧を返しました。(実際のテーブル名は伏せております)
- テーブルバケットに Namespace 配下で6つのテーブルがあります。
テーブル名 作成日 最終更新 検証用テーブル 3/27 13:06 3/27 13:06 断片化テスト用テーブル 3/27 11:13 3/28 05:36 ... ... ...
API の呼び出し順序やパラメータの組み立ては AI アシスタントが自動で処理するため、ユーザーは自然言語で問いかけるだけです。
実演2: スキーマを知らなくてもクエリできる
Q: 「2026年1月と2月を比較して、レコード数と合計金額の差異を見たい」
AI アシスタントは最初 誤ったカラム名で SQL を生成しましたが、カラム名エラーが発生したため、自動で SELECT * LIMIT 1 を実行してスキーマを確認し、正しいカラム名に修正して再実行してくれました。
月 レコード数 売上合計 平均売上 01 225,834 約1.14兆円 約506万円 02 209,376 約1.12兆円 約536万円
ユーザーはカラム名を知らなくても、業務用語(「合計金額」)で指示するだけで正しい結果を得られます。
従来 Iceberg では同じ体験が難しい理由
2026年3月時点で、AWS は S3 Tables 向けの MCP サーバーを公式に提供していますが、従来 Iceberg(Glue Data Catalog + Athena)向けの MCP サーバーは提供されていません。
| 観点 | S3 Tables | 従来 Iceberg |
|---|---|---|
| 公式 MCP サーバー | AWS 提供 | なし(自前実装が必要) |
| クエリ実行 | PyIceberg + Daft で同期実行 | Athena の非同期 API をラップする実装が必要 |
| メンテナンス管理 | 専用 API で設定・状態を取得可能 | マネージド API なし |
| セットアップ | uvx コマンド1行 |
MCP サーバーの設計・実装・テストが必要 |
S3 Tables への移行は、AI アシスタントとの統合という観点でも、従来 Iceberg では得られなかった開発体験を提供します。
まとめ
今回は、既存の Apache Iceberg on S3 環境に S3 Tables を並行導入し、移行方針の比較と実測検証を行いました。
- 移行方針: S3 Tables への移行はフルデータ移行が前提。並行運用(デュアルライト)により、既存環境を壊さず検証が可能で、問題発生時のロールバックも容易
- 実装上の注意: Athena × S3 Tables には DDL構文の違い、ハイフン問題、IAM の名前空間の不一致など、ドキュメントだけでは読み取りにくいポイントがある
- 性能: 小規模テーブルでは有意差なし。S3 Tables の真の価値は「性能劣化の自動防止」にある
- 料金: ストレージ単価は約15%高い。Intelligent-Tiering をテーブル単位で有効化できるのは扱いやすい
- 運用: 自動コンパクション・スナップショット管理・不要ファイル削除により、メンテナンスの運用コストをゼロにできる
- MCP: 公式 MCP サーバーにより、AI アシスタントからの自然言語操作が可能。従来 Iceberg にはない開発体験
S3 Tables は「性能が劇的に速くなる」サービスではなく、「放っておいても性能が劣化しない」サービスです。テーブル数が増え、メンテナンスの運用負荷が課題になっている環境ほど、移行のメリットを実感できると思います。
今後は、CDK の L2 コンストラクト対応など、S3 Tables の進化を引き続き追いかけていきたいと思います。
