ブログ

【AWS】S3 Tablesでどこまで速くなる?S3 Tablesの自動compaction検証

この記事をSNSでシェア!

はじめに

幕張メッセで行われたAWS Summit Japan 2026に、ビジネスソリューション事業部の3名で現地参加してきました。今年はAWS All Certifications Engineers 2026に選出いただき、会場で副賞も受け取ってきました。(同じ部署メンバーの参加レポートはこちらの記事からご覧ください)
認定資格の学習で得た知識を実際のアーキテクチャ検討に還元する良い機会と捉え、2日間、データ分析基盤に関するセッションとブースを中心に回ってきました。
というのも、いま支援しているお客様のデータ分析基盤に、持ち帰りたい課題があったためです。
その基盤は「S3にParquetを置き、Athenaでクエリする」というシンプルな構成です。導入当初は十分に機能していましたが、データが数年分積み上がるにつれて、2つの問題が目立つようになってきました。

  1. 小ファイルの堆積。バッチが数MBのParquetを随時書き込むため、ファイル数だけが増え続け、クエリが徐々に遅くなる
  2. パーティションキー以外での絞り込みが遅い。日付パーティションなので期間指定は速い一方、「特定デバイスの全履歴」のような依頼が来ると全期間のフルスキャンになる

Summitのセッションで聞いたAmazon S3 Tablesの自動メンテナンスは、この2つの課題にちょうど対応する内容でした。「Apache Icebergのファイル統計によってパーティションキー以外の絞り込みが効くようになり、S3 Tablesのマネージドcompactionが小ファイルを自動で統合してくれる」というアプローチです。

ただし、この種の「筋書きどおりなら効くはず」は、実測してみると条件付きだったということがよくあります。ダミーデータ1.5億行で検証環境を構築し、素のParquet / 素のIceberg / S3 Tablesの3構成に同一データ・同一クエリを投げて比較しました。

先に結果の要点を示します。

  • S3 Tablesの自動sort compaction後、非パーティションキーの絞り込みクエリのスキャン量が99.2%削減された
  • 一方、Icebergに移行しただけの構成では、スキャン量・実行時間とも一切改善しなかった

本記事では、この「効いた条件」と「効かなかった条件」の切り分けを具体的な検証結果としてご紹介します。

対象読者

  • S3 + Parquet + Athena構成の性能・コストに頭打ち感がある方
  • S3 TablesやApache Icebergの導入効果を、定量的に把握したい方
  • 小ファイル問題への対処を、メンテナンスジョブを自作せずに済ませたい方

本記事で取り扱わないこと

  • S3、Athena、Glueの基本概念や構築手順
  • Icebergのスナップショット管理・タイムトラベル・スキーマ進化
  • TB級スケールでの検証(今回は6〜8 GiB / 1.5億行のダミーデータです)
  • ベンチマークの統計的な厳密性(各クエリ5回実行の中央値です)

前提知識:プルーニングが効くには「物理配置」が必要

検証結果を読み解くうえで必要な前提は、次の1点だけです。
Icebergはデータファイルごとに各カラムのmin/max統計を持ち、クエリ条件と照合して不要なファイルをスキャン前に除外します(ファイルプルーニング)。「パーティションキー以外でも絞り込みが効く」と言われる根拠はこの仕組みです。
ただし、この統計が機能するには条件があります。たとえばdevice_id = ‘DEV-00042’で絞り込む場合、全ファイルに1,000種類のデバイスが混在していると、すべてのファイルのmin/max範囲に該当値が含まれるため、1ファイルも除外できません。プルーニングは「ファイルごとに値がまとまっている」物理配置があって初めて機能します。
その物理配置を作る手段がsort compactionです。S3 TablesはIceberg専用のマネージドストレージで、compactionやスナップショット管理を自動実行します。テーブルにsort orderを定義しておくと、compaction時にその順序でデータを並べ替えて書き直します。

検証の設計

比較する3構成

「Icebergというフォーマットの効果」と「S3 Tablesの自動メンテナンスの効果」を分離するため、中間に「素のIceberg」を挟んだ3構成としました。

構成内容パーティションcompaction
A素のParquet外部テーブル(Glueカタログ)Hive形式(文字列)なし
B通常のS3上のIceberg(Glueカタログ)day(record_time)なし
CS3 Tables上のIcebergday(record_time)自動(sort戦略、target 128 MiB)

Cにはsort orderとしてdevice_idを指定しています。

検証データ

IoTデバイスのテレメトリを模したダミーデータです。期間90日・1.5億行・約6.22 GiB、カラムはdevice_id、record_time、センサー値など計9個です。
データ設計では、冒頭で挙げた課題の状況を意図的に再現しています。

  • 小ファイルの堆積:毎時バッチを模した約3〜4MBのファイル2,160個(24ファイル/日 × 90日)
  • プルーニングが効かない配置:1,000種類のdevice_idを全ファイルにランダムに混在

クエリと計測方法

Athenaから5種類のクエリを各構成に5回ずつ実行し、中央値を採用しました。

No内容見たいもの
Q1全件の単一カラム集計(AVG, STDDEV)小ファイルの影響
Q2全件の複数カラム集計 +GROUP BY同上
Q37日間の期間絞り込みパーティションプルーニング
Q4単一device_idでの絞り込み本命:非パーティションキーのプルーニング
Q5device_id 100個分の範囲絞り込み同上

本命はQ4で、次の単純なクエリです。

SELECT COUNT(*), AVG(speed_kmh)
FROM <table>
WHERE device_id = 'DEV-00042'

計測は、T0(投入直後=全構成が小ファイル状態)T1(Cの自動compaction完了後)の2時点で実施しました。

実装の概要

環境はCDK(TypeScript)で構築しました。B/Cへの書き込みはPyIceberg(0.11.1)で、CへはS3 TablesのIceberg RESTカタログエンドポイント経由で接続しています。小ファイルは、テーブルプロパティwrite.target-file-size-bytesを約4MBに設定し、1日分を1コミットで20数ファイルに分割させる方法で再現しました。
なお、S3 Tablesのcompactionには手動トリガーAPIがなく、実行タイミングは制御できません。事前調査では「書き込み停止後2.5〜3時間で実行された」という内容も見られましたが、今回はcompaction有効化から数分でジョブが完了しました。実行タイミングはAWS側のスケジューリングに依存すると考えておくのが無難です。

検証結果

結果1(T0):Iceberg移行だけでは変化無し

compaction前の計測結果です。この表で注目すべき点は1つだけ、BとCのスキャン量が全クエリで完全に一致していることです。

スキャン量(bytes)

QueryA(素Parquet)B(素Iceberg)C(S3 Tables)
Q11,115,723,5631,398,949,7761,398,949,776
Q22,287,577,6322,858,605,2382,858,605,238
Q386,783,156108,812,368108,812,368
Q41,313,491,1031,596,864,8521,596,864,852
Q51,313,491,1031,596,998,6081,596,998,608

Cにはsort orderを定義済みですが、Bとの差は1バイトもありません。sort orderは「宣言」にすぎず、既存ファイルの物理配置を変えないため、compactionが走るまでは何の効果もありません。実際、device_idで絞り込むQ4のスキャン量は、全件集計のQ1とほぼ同じ約1.5GBです。プルーニングは機能していません。
実行時間に関しては、この時点では素のParquet(A)が全クエリで最速でした。

エンジン実行時間(中央値、ms)

QueryABC
Q12,7743,0943,087
Q23,6384,2464,567
Q31,3402,2761,549
Q42,5452,7392,852
Q52,1562,5982,641

補足すると、B/CはPyIcebergで書き直した結果、物理サイズがAより約27%大きくなっており、実行時間の差はほぼこの影響だと考えられます。よって「Icebergだから遅い」とは言えませんが、「Icebergに変えただけで速くなる」ということはありません

結果2:自動compactionで2,250ファイル → 90ファイル

Cのcompactionを有効化すると、数分でジョブのステータスがSuccessfulとなり、ファイル構成が次のように変わりました。

構成T0ファイル数T1ファイル数T0サイズ(bytes)T1サイズ(bytes)
A2,1602,1606,674,783,3836,674,783,383
B2,2502,2508,489,767,6248,489,767,624
C2,250908,489,767,6246,345,321,971

ファイル数は96.0%削減。加えてデータサイズも25.3%縮小しました。ソートにより類似の値が隣接して圧縮効率が上がり、書き直しで27%増えていたサイズが元のParquetを下回るまで戻った形です。
ここで強調したいのは、この間にユーザー側が行った操作は「compactionの有効化」のみという点です。GlueジョブもOPTIMIZE文も不要でした。小ファイル対策のメンテナンスを運用として持たなくてよいことが、S3 Tablesによる運用負荷軽減の中で最も大きい価値だと言えます。

結果3(T1):本命クエリのスキャン量が99%削減

compaction後のCのBefore/Afterです。

Query実行時間 T0→T1(ms)変化スキャン量 T0→T1スキャン削減
Q13,087 → 2,47319.9%短縮1.30 GiB → 1.04 GiB20.3%
Q24,567 → 2,88736.8%短縮2.66 GiB → 2.13 GiB20.0%
Q31,549 → 1,551ほぼ不変104 MiB → 83 MiB20.3%
Q42,852 → 1,83035.8%短縮1.49 GiB → 12.9 MiB99.2%
Q52,641 → 2,27313.9%短縮1.49 GiB → 115 MiB92.4%

Q4(単一デバイスの絞り込み)のスキャン量は約1.49 GiBから約12.9 MiBへ、99.2%減少しました。device_idでソートされた90ファイルのうち、対象デバイスを含む一部のファイルだけを読めばよくなったためです。デバイス100台分の範囲を絞り込むQ5でも92.4%の削減です。compaction前は全件集計と同量を読んでいたクエリが、必要な分だけを読むクエリに変わりました。
Athenaはスキャン量課金のため、この結果は該当クエリの単価が約1/100になったことを意味します。特定デバイスの調査のような非パーティションキーの絞り込みが日常的に発生する環境では、性能以上にコスト面の改善として効いてきます。
一方で、全てが劇的に改善するわけではなく、以下の2点が注意点として挙げられます。

全件集計(Q1/Q2)の削減は約20%にとどまります。 これはプルーニングではなく圧縮効率改善の副産物です。全件を読むクエリは、ファイルをどう並べ替えても読む量は減りません。sort compactionは「絞り込みのあるクエリ」に効く技術です。

スキャン量99%減でも、実行時間の短縮は36%程度の改善に収まっています。 Athenaにはクエリ起動・プランニング・結果書き出しなど、データ量に依存しない固定のオーバーヘッドがあり、今回の検証データ規模ではこれが支配的なためです。裏を返せば、データがTB級になりスキャン時間が支配的になるほど、この削減は実行時間・コストの双方へ影響してきます。

考察

検証で以下の3点が確認できました。

  1. Iceberg移行だけでは改善しない:compaction前のB/Cはスキャン量が完全一致し、素のParquetより遅かった
  2. sort compaction後、非パーティションキーの絞り込みが激変する:スキャン量99.2%減、クエリ単価約1/100
  3. S3 Tablesはこれを全自動で行う:2,250→90ファイル、ユーザー側の運用ジョブなし

冒頭の課題に照らすと、小ファイル問題は自動compactionによりメンテナンスジョブを自作せずに解消でき、「特定デバイスの全履歴」型のクエリはsort orderの定義で大幅に改善できる見込みが立ちました。素のIcebergでもAthenaのOPTIMIZE文で同じ状態は作れますが、スケジュール管理・実行コスト・失敗時対応が運用負荷として残るため、マネージドで最適化され続けることの価値は大きいと感じています。
設計上の留意点として、sort orderはテーブルに1セットしか定義できません。導入にあたっては「どのカラムでの絞り込みが多いか」というクエリパターンの棚卸しが前提になります。絞り込みカラムが複数軸にわたる場合はz-order戦略という選択肢もあり、これは今後の検証候補です。

ハマりどころメモ

検証中に遭遇したポイントです(PyIceberg 0.11.1、2026年7月時点の事象)。

  • S3 TablesとAthena/Glueの統合はアカウント・リージョン単位の有効化が必要。Athenaからはカタログにs3tablescatalog/<table-bucket名>、データベースにnamespaceを指定する
  • Lake Formation権限とIAM権限は別物で、両方の確認が必要
  • S3 TablesのテーブルはS3 APIで直接読み書きできない(Icebergクライアント経由のみ)。テーブル削除はpurge付きで行う
  • S3 Tables REST APIのnamespace一覧レスポンスとPyIceberg 0.11.1の間で互換性の回避策が必要だった

検証の制約

本検証から言えないことも明記しておきます。

  • ベンチマークはクエリの実行統計のみを記録しており、集計結果値の全構成一致までは照合していません(同一の生成元データを投入し、全150クエリの成功は確認済み)
  • 実行時間は5回の中央値で、時間帯を変えた反復や実行順のランダム化は行っていません
  • TB級スケール、書き込みとcompactionの並行動作、z-orderは未検証です

まとめ

AWS Summit Japan 2026で得た知見をもとに、「S3 Tables + Icebergで既存データ分析基盤の課題を解決できるか」を3構成・1.5億行のダミーデータで実測しました。
得られた結果は想定以上でした。非パーティションキーの絞り込みはsort compaction後にスキャン量99.2%削減という大きな効果を示した一方、compaction前のIcebergは素のParquetと比べて何の優位も示しませんでした。「Icebergにすれば速くなる」ではなく、「統計が効く物理配置を作る仕組みとセットにして初めて速くなる」。この条件の理解が、今回の検証で最も価値のある収穫だったと考えています。カタログスペックや講演資料だけでは、この解像度には至れませんでした。
S3 TablesとIcebergの採用を検討される際は、まず自環境のクエリパターン(絞り込みに使うカラム、全件集計の頻度)を棚卸しし、小規模なダミーデータで構わないので今回のようなBefore/Afterを実測することをおすすめします。ぜひ本記事を、皆さまの検証、環境最適化にお役立てください。

参考文献

投稿者プロフィール

久保 研斗
久保 研斗
AWSをメインとするバックエンドエンジニア。要件定義から運用保守まで幅広く対応しています。趣味は競技プログラミングで、アルゴリズムやロジックのパフォーマンスチューニングが得意。
この記事をSNSでシェア!