本記事はDmitriy Fingerman氏が2025年6月2日に公開した『Hive Iceberg Compaction: An In-Depth Look at Improving Table Performance』を、同氏のご厚意により許可を得て翻訳・掲載したものです。原文の内容・著作権は同氏に帰属します。誤訳等ございましたらお知らせください(X, Linkedin)。改めてDmitriy Fingerman氏の寛大なご協力に深く感謝いたします。
This article is an authorized translation of “Hive Iceberg Compaction: An In-Depth Look at Improving Table Performance” by Dmitriy Fingerman, published on June 2, 2025. It is reproduced here with the author’s kind permission. Copyright for the original text remains with Dmitriy Fingerman. If you spot any translation errors, please let me know(X, Linkedin). My sincere thanks to Dmitriy Fingerman for graciously allowing this translation.
Apache HiveはIcebergテーブルに対するコンパクション機能を提供しています。コンパクションはIcebergテーブルへのクエリ性能を向上させるように設計されており、merge-on-readモードでIcebergテーブルを扱う際に特に効果的です。この新機能はストレージの格納効率とクエリ時の性能を同時に最適化します。本記事では、その基本機能と様々なHive Iceberg Compaction実行方法について掘り下げ、クエリ性能にどのような影響があるのか実例を用いて解説します。
- 前提となる知識
- なぜHive Iceberg Compactionが必要なのか
- Hive Iceberg Compactionとは
- Hive Iceberg Compactionが実行される流れ
- Iceberg Compactionに関連する文法
- コンパクションの種類
- Icebergコンパクションコマンドの実行方法
- コンパクション条件の判定
- パフォーマンステストの結果: Compaction in Action
- 注目すべき観察結果
- 結果
前提となる知識
この記事はApache Hive及びApache Icebergへの基本的な理解を前提としています。これらのツールに馴染みがない場合は以下のページを先にご一読ください。
なぜHive Iceberg Compactionが必要なのか
Icebergテーブルに対するクエリ性能は以下の状況で劣化する場合があります。
- テーブルやパーティション内に大量の小さなファイルが存在する場合: 小さなファイルの数が膨大になると、クエリ実行時に発生するI/O回数が急激に増加し、クエリ効率に負の影響が発生します。
- 削除ファイルが存在する場合: Icebergのmerge-on-readモードはレコード削除情報を位置削除ファイル(position delete file)に書き込むことでレコードの削除を表現します。このアプローチは書き込みが支配的なワークロードにおいて効率が良いものの、読み込み時の性能に大きな影響を与えます。あらゆるクエリが実データを読むたびに削除ファイルをスキャンする必要があり、大きなオーバーヘッドが発生するからです。
Hive Iceberg Compactionとは
Hive Iceberg Compactionは既存のデータファイルと削除ファイルを新たなデータファイルに書き直すことでこれらの問題を解決します。新たに作成されるデータファイルはコンパクション実行時点のテーブルの状態に最適化されています。つまり各ファイルは適度に大きく、総ファイル数は少なく、そして削除ファイルが存在しない状態になるということです。
Hive Iceberg Compactionが実行される流れ
コンパクションの実行プロセスはいくつかのステップに分解することができます。
- コンパクションリクエストの開始: Icebergコンパクションコマンドを手動で実行すると、Hiveはそのコマンドをパーズ・解析し、コンパクションを開始します。
- コンパクションリクエストの生成及びキューイング: 次にHiveは具体的なコンパクションリクエストを構築します。テーブルがパーティション化されていない場合、テーブル全体を更新する単一のコンパクションリクエストが作成され、パーティション化されている場合はパーティションごとにリクエストが作成されます。現在のテーブル状態がパーティション進化(partition evolution)により複数種類のパーティション定義を含んでいる場合、過去の定義により作成されたパーティションもリライト対象となります。これらのリクエストはHive Metastore(HMS)にキューイングされます。
- コンパクションの実行: キューイングされたコンパクションリクエストは以下の仕組みを通して実行されます。
- コンパクション条件の評価: まず対象となるテーブルやパーティションがコンパクション条件を満たしているか確認します。条件とは、例えば小さなファイルや削除ファイルが十分な数存在するかどうか、などを含みます。
- SQLの生成: 条件を満たす場合、Hiveは
INSERT OVERWRITE
クエリを動的に生成します。このクエリは対象となるテーブルやパーティションに存在するデータを書き直し最適化するように組み立てられます。 - コンパクションクエリの実行: 最後に生成されたSQLを実行し、実データのマージや最適化を行います。
ディスク使用量への影響: スナップショットやファイルの削除
コンパクションは現時点のデータセットに存在するデータを書き直しかつ削除レコードを取り除きます。これに関連して、Icebergテーブルがディスクスペースを解放する仕組みについて留意すべきことがあります。Icebergテーブルはスナップショットを前提としたアーキテクチャになっており、コンパクションを含むあらゆる更新は現在アクティブなデータセットへの参照を保持する形で新たなスナップショットを生成します。
そのため、古いスナップショットに含まれるデータファイルや削除ファイルがコンパクションにより即座に取り除かれるわけではありません。むしろIcebergはそれらをテーブルの操作履歴とともに保存することで、テーブルの過去の状態に対してクエリを実行するタイムトラベルのような操作を可能にしています。
古いファイルを物理的に削除しディスクスペースを解放するには、古いスナップショットを失効させるコマンドを明示的に実行しなければなりません。この操作は不要になったスナップショットを、典型的にはそのタイムスタンプやスナップショットIDを元に削除します。あるデータファイルや削除ファイルを参照するスナップショットが存在しなくなったときに初めて、それらのファイルが物理削除されます。
Iceberg Compactionに関連する文法
Hive Iceberg Compactionは二種類のコマンドを提供します。Hive ACIDテーブルのコンパクションでおなじみの ALTER TABLE
コマンドと、新たに導入された OPTIMIZE TABLE
コマンドです。
ALTER TABLEコマンド
ALTER TABLE
コマンドはHive ACIDテーブルに対するコンパクションと同じ文法をIcebergテーブルに対して提供します。
ALTER TABLE <table_name> [PARTITION <partitionSpec>] COMPACT 'MAJOR'|'MINOR'|'SMART_OPTIMIZE' [AND WAIT] [POOL <poolName>] [WHERE <condition>] [ORDER BY <orderBySpec> [DESC]]
OPTIMIZE TABLEコマンド
OPTIMIZE TABLE
コマンドはIcebergテーブルをコンパクションするために導入された新たなコマンドです。これはTrinoやImpalaのようなクエリエンジンと似た使い勝手を提供します。
OPTIMIZE TABLE <table_name> REWRITE DATA [POOL <poolName>] [WHERE <condition>] [ORDER BY <orderBySpec> [DESC]]
コンパクションの種類
Hiveは様々な最適化シナリオに対応するために、三種類のコンパクションを提供します。
- MINORコンパクション: 多数の小さなデータファイル群をより大きく効率的なファイルに書き換え、現在のスナップショットが参照するファイルの数を減らします。設定した閾値を下回るデータファイルがリライトの対象となります。
- MAJORコンパクション: そこそこの大きさだけど理想的とまではいえないファイルが多数ある場合や、大量の削除ファイルが蓄積されている状況で実行するコンパクションです。テーブルやパーティション内にある全ファイルがリライトの対象となります。
- SMART_OPTIMIZEコンパクション: 現在のテーブルやパーティションの状態、または特徴をベースにMAJORコンパクションかMINORコンパクションを自動で選択し実行します。
Icebergコンパクションコマンドの実行方法
テーブル全体のコンパクション
コンパクション対象とするパーティションが指定されていない場合、テーブル全体をコンパクションします。これは関連するすべてのデータを対象とするコンパクションリクエストを生成し、実行します。
コマンド実行例:
ALTER TABLE 'store_sales' COMPACT 'MAJOR';
コンパクションコマンドにフィルターを指定する
Hive Iceberg Compactionは WHERE
句によるフィルタリングをサポートします。フィルターが指定されると、その条件にマッチするレコードを持つパーティションのみが対象となるようにコンパクションリクエストを生成します。フィルターが参照するカラムはパーティション定義に存在するものでなくてはいけません。
コマンド実行例:
ALTER TABLE store_sales COMPACT 'MAJOR' AND WAIT WHERE (channel IN ('A', 'B') AND sold_date < '2024–09–01') OR (channel IN ('C', 'D') AND sold_date > '2024–08–01');
コンパクションコマンドに対象パーティションを指定する
特定のパーティションのみをコンパクションの対象とすることも可能です。これはテーブルのどの部分を最適化するかを細かくコントロールしたい場合に便利です。
コマンド実行例:
ALTER TABLE 'store_sales' PARTITION(region='NA', channel=1, sold_date='2025-03-20') COMPACT 'MAJOR';
コンパクションコマンドにソート条件を指定する
Hiveは最適化の一環として、コンパクション時にデータをソートすることが可能です。データのリライト時にソートを行うと、範囲ベースでのスキャンや結合処理が非常に高速になる可能性があります。
コマンド実行例:
ALTER TABLE store_sales COMPACT 'MINOR' ORDER BY sale_timestamp;
コンパクションコマンドにカスタムプールを指定する
Hive ACIDテーブルに対するコンパクションと同様に、Hive Iceberg Compactionはカスタムコンパクションプールを活用することができます。これにより特定のコンパクションリクエストを指定したワーカースレッドにスケジュールすることができます。この機能はリソース管理を強化したり、コンパクション処理を他のクリティカルな操作から分離したりするために使用することができます。
カスタムコンパクションプールはコンパクションコマンドで直接指定するか、あるいはテーブルやデータベース単位で設定しておくことができます。
コンパクションコマンドで指定する場合は次のように実行してください。
ALTER TABLE store_sales COMPACT 'MINOR' POOL 'iceberg';
データベースレベルでカスタムコンパクションプールを設定することも可能です。この設定値はそのデータベースに存在するあらゆるテーブルをコンパクションする際に用いられます。ただし、テーブルレベルでもカスタムコンパクションプールが指定されている場合はそちらが優先されます。
CREATE DATABASE ice_test WITH DBPROPERTIES('hive.compactor.worker.pool'='iceberg'); USE ice_test; -- Assuming you want to use the newly created 'ice_test' database
テーブルレベルでカスタムコンパクションプールを指定する例です。
create table ice_orc ( first_name string, last_name string ) stored by iceberg stored as orc tblproperties ('hive.compactor.worker.pool'='iceberg');
コンパクションプールの詳細は『Apache Hive : Compaction pooling』に記載されています。
コンパクション条件の判定
コンパクション条件はコンパクションリクエストを実行する最初のステップで評価されます。この段階で、対象となるテーブルやパーティションが指定されたコンパクション(MAJOR、MINOR、もしくはSMART_OPTIMIZE)を本当に必要としているかが判定されます。もしコンパクションがまだ不要である場合、そのコンパクションリクエストは単純にスキップされ、「refused」というステータスで終了します。
内部的な説明をすると、コンパクション条件はApache Amoroによる実装を活用して評価されます。例えばAmoroによる評価アルゴリズムが MAJOR OPTIMIZE
を必要とすると判断した場合に、Hiveはメジャーコンパクションが必要であると判断します。同様にAmoroが MINOR OPTIMIZE
が必要だと判断する場合、Hiveはマイナーコンパクションを実行します。
Amoroの最適化戦略について詳しく知りたい場合はApache Amoro公式ドキュメントの『Self-optimizing』を読むとよいでしょう。
Amoroによる判定: キーポイント
Amoroの判定システムはいくつかの閾値をベースにファイルの種類を識別し、賢いコンパクションを実現します。
- Fragment File:
compactor.threshold.target.size * compactor.threshold.fragment.ratio
を下回る非常に小さなIcebergデータファイルを指します。デフォルトでは16MBを下回るデータファイルがこれに分類されます。この条件に該当するファイルはマージの主な対象となります。 - Min Target Size Threshold:
compactor.threshold.target.size * compactor.threshold.min.target.size.ratio
により計算される閾値です。デフォルトは96MBで、Segment Fileの下限値となります。 - Undersized Segment File: Fragment Fileより大きいものの、Min Target Size Thresholdより小さなデータファイルはこれに分類されます。つまり、デフォルト設定では16MB ~ 96MBのデータファイルを指します。これらは最適化されていないとみなされ、リライト対象となる可能性があります。
- Segment File: Min Target Size Thresholdを満たすデータファイルを指します。つまり、デフォルト設定では96MB以上のファイルがこれに分類され、通常適切な状態だと判定されます。
マイナーコンパクションが実行される条件
マイナーコンパクションは主に小さなデータファイル群をマージするために実行されます。
- Fragment Fileの数が
compactor.threshold.min.input.files
を上回る場合。デフォルトの閾値は12です。これはマージすべき非常に小さなデータファイルが多数存在する場合に該当します。
メジャーコンパクションが実行される条件
メジャーコンパクションはより広範なデータファイルが非効率になっている場合や大量の削除ファイルが存在する場合に実行されます。
- Undersized Segment Fileの合計サイズが
compactor.threshold.target.size
を上回る場合。デフォルトの閾値は128MBです。つまり、最適でないサイズのファイルが大量に存在する場合に該当します。 - 最小のUndersized Segment File2つを合算したサイズが
compactor.threshold.target.size
を下回る場合。つまりマージにより最適化が行える場合がこれに該当します。 - いずれかのFragment Sizeの閾値より大きなデータファイルから、
compactor.threshold.delete.file.ratio
を上回る割合のレコードが削除されている場合。デフォルトの閾値は10%です。つまり、大量のレコード削除によりクエリが非効率になってしまっている場合がこれに該当します。
コンパクション判定に用いられるテーブルレベルプロパティ
Amoroによるコンパクション判定の動作はHiveのテーブルレベル、もしくはデータベースレベルプロパティでコントロールできます。
compactor.threshold.target.size
: コンパクション処理が生成しようとする理想的なデータファイルサイズです。デフォルト値は128MBです。compactor.threshold.fragment.ratio
: 非常に小さなFragment Fileを識別するために用いられる閾値です。デフォルト値は1/8なので、compactor.threshold.target.size
がデフォルトの128MBの場合、16MBを下回るファイルがFragment Fileであると認識されます。compactor.threshold.min.target.size.ratio
: Segment Fileであるか判定するために用いられる閾値です。デフォルト値は3/4なので、compactor.threshold.target.size
がデフォルト値の場合96MBがSegment Fileかどうかを判定するための閾値となります。compactor.threshold.min.input.files
: 小さなファイルの数がマイナーコンパクションを実行するために必要な量を満たしているか判定するために使われます。デフォルト値は12です。compactor.threshold.delete.file.ratio
: 削除されたレコード数とデータファイル内のレコード数の比率。デフォルト値は10%で、これを超えるとメジャーコンパクションが実行されます。
パフォーマンステストの結果: Compaction in Action
HiveによるIcebergテーブルコンパクションがもたらす実世界での利点を実演するために、我々はパフォーマンステストを実施しました。これはコンパクション前と後でクエリ実行時間がどのように変化するかを比較したものです。そしてコンパクションは単純な性能改善以上の効果をもたらします。
コンパクションの実行時間
コンパクション処理に必要とした時間は次のとおりです。
- 実行時間: 84秒
- キューイング中の時間を含む実時間: 127秒。これはシステム負荷と利用可能なリソース量に依存します
コンパクションはある程度の時間を要しますが、その恩恵はコストを凌駕します。
性能評価に用いたクエリ
読み込み性能を計測するために、WHERE
句を含む COUNT(*)
クエリを実行しました。
SELECT count(*) FROM <table_name> ss WHERE 'col1' = 'value1' AND 'col2' = 'value2'
注意点: この手のクエリの性能はファイル数と削除レコードを処理するオーバーヘッドに大きく左右されます。
劇的な効果: 重要な統計情報一覧
次の表はコンパクション前後でデータがどのように変化したかを示しています。その違いは歴然としています。
| Metric | Before Compaction | After Compaction | |-------------------------|-------------------|------------------| | Data Records | 8,655,041 | 7,648,151 | | Positional Deletes | 1,006,890 | 0 | | Data Files | 1,114 | 1 | | Positional Delete Files | 8 | 0 |
性能比較: クエリの速度
次の表はコンパクション前後、また位置削除情報のキャッシュの有無別にクエリ実行時間を示しています。
| Scenario | DAG Run Time (seconds) | |------------------------------------------|------------------------| | Before Compaction, Caching Disabled | 92.5 | | Before Compaction, Caching Enabled | 5.3 | | After Major Compaction, Caching Disabled | 2.2 | | After Major Compaction, Caching Enabled | 2.2 |
注目すべき観察結果
我々のテストにより、いくつかの重要な洞察がもたらされました。
- 削除情報を処理するオーバーヘッドを排除: コンパクションは1,006,890に及ぶ削除情報と位置削除ファイルをアクティブなデータセットから取り除き、読み込みが高速になりました。
- ファイル数の大幅な削減: コンパクションは1,114個のデータファイルを単一の最適化されたファイルにマージしました。これにより読み込み時のI/Oの回数が劇的に減少しました。
- 物理データのクリーンアップ: コンパクションはファイルを単にマージするだけでなく、100万を超える削除済みレコードを取り除いた状態で新たなデータファイルを作成します。これにより最新のデータセットはより小さく最適化された状態になりました。我々のテスト結果はデータファイル内に存在するレコードが8,655,041行から7,648,151行に減少したことを示しており、論理削除されていたデータが最新データセットから物理的にも取り除かれていることが伺えます。
- キャッシュ単体以上の効果: Icebergの位置削除情報のキャッシュは性能を大きく改善しますが、メジャーコンパクションによる改善はより協力で、実行時間は最終的にわずか2.2秒になりました。コンパクションはキャッシュ以上の効果をもたらしうるということです。
まとめると、Hive Iceberg Compactionはファイルをマージし、論理削除されたレコードをアクティブなデータセットから取り除き、また全体的なテーブル構造を最適化することで相当な性能改善をもたらすことが実験結果から伺えます。この劇的なデータファイル数の削減や削除情報を反映するオーバーヘッドの排除は、クエリの高速化や効率化に直接寄与します。
結果
Hive Iceberg CompactionはIcebergテーブルに対するクエリ性能を最適化し巨大なデータセットを効率的に管理するための、強力で必要不可欠な機能です。小さなファイル群を賢くマージし、最新のデータセットから論理削除されたレコードを取り除き、そしてMINOR, MAJOR, SMART_OPTIMIZEモードを戦略的に活用することで、クエリの実行時間とストレージ格納効率を格段に改善することができます。内部で使用しているAmoroはコンパクションを実行すべきかどうかを設定値に応じて賢く判定します。
我々のパフォーマンステストが示したように、コンパクションは高速なデータアクセスを実現し分析ワークロードの応答速度を改善することで、クエリ性能を劇的に高めます。Hive Iceberg Compactionはハードで大規模な環境にて最適なパフォーマンスを提供し続ける上で決定的に重要で欠かせない機能です。