はじめに
レイクハウスの中核として、Iceberg・Delta・Hudiの3つは事実上の定番になりました。どれもオブジェクトストレージ上でACIDやタイムトラベルを実現し、DWHとデータレイクの間を埋める役割を担います。
ただし本番で差が出るのは、カタログ連携やエンジン互換だけでなく、更新系ワークロード、コンパクション運用、スキーマ進化の癖、そして障害時の復旧体験です。
この記事は「結局どれを選べばいいの?」に対して、用途別の最短ルートを提示することを目的にしています。
TL;DR
| ユースケース | 推奨 | 理由 |
|---|---|---|
| 分析中心でマルチエンジン前提 | Iceberg | 隠蔽パーティショニングやパーティション進化、スキーマ進化が強く、レイアウト変更を運用で吸収しやすい |
| Spark中心でバッチとストリーミングを統一したい | Delta | Sparkと深く統合されたオープンソースのストレージレイヤとして広く使われている |
| アップサートやCDCなど更新系が主役 | Hudi | Copy-on-WriteとMerge-on-Readなど更新に最適化した設計が強み |
選定マトリクス
| 要件 | 第一候補 | 理由 |
|---|---|---|
| 大規模分析、複数クエリエンジン、レイアウト変更を頻繁にしたい | Iceberg | 隠蔽パーティショニングと進化前提の設計 |
| Spark中心、運用をシンプルに寄せたい | Delta | Sparkのエコシステムと整合しやすい |
| 高頻度アップサート、CDC、近リアルタイムの増分処理 | Hudi | 更新系の設計思想が最初から強い |
この時点で8割は決まります。残り2割は「運用の地雷」を踏まえて調整する感じです。
3方式の思想の違い
Iceberg
「テーブルの論理と物理を分離」する思想が濃い
- ユーザーは列に対して自然にクエリを書く
- パーティショニングや物理レイアウトの変更はメタデータで吸収する設計(隠蔽パーティショニング)
- 公式仕様でスキーマ進化とパーティション進化を中核機能として扱う
Delta
Sparkの信頼性をレイクに持ち込む方向
- オープンソースのストレージレイヤとしてACIDやタイムトラベルを提供
- Sparkワークロードを安定運用したい組織にハマりやすい
Hudi
更新系と増分処理の実戦主義
- COW/MORなどの選択肢を持つ
- アップサートや削除、増分クエリを現実的に回すための仕組みが中心
- 近年もエンジン対応や性能改善が続いている
本番で重要な論点 7つ
1. 更新がどれだけ多いか
- 1日1回の追記中心なら、どれでも成立
- キー付き更新や削除が多いなら Hudi優先
- 追記中心でも「後から訂正が入る業務」は案外多いので、将来の仕様変更も含めて考える
2. クエリエンジンの多様性
- Sparkだけで完結するなら Deltaが楽
- Trino/Presto、Flink、Dremio など混在するなら Icebergが安心寄り
- 組織の標準エンジン戦略とセットで決める
3. パーティション戦略の寿命
パーティションは必ず陳腐化します。 ビジネスが変わると、最適な切り方も変わります。
- パーティション進化を前提にした設計が必要
- Icebergはこの変更を自然に扱える思想が強い
4. コンパクション運用
どの方式でも「小さなファイル地獄」は避けられません。
運用の差が出るのは以下の点です。
- いつ
- どの粒度で
- どのSLAで
コンパクションを回すか。実装よりも運用ポリシーと監視が重要です。
5. スキーマ進化のルール
追加は簡単でも、Renameや型変更は落とし穴になりやすいです。
「データ契約」を作って以下をCIに入れると事故が激減します。
- 変更申請
- 互換性テスト
- 利用者影響の可視化
6. カタログとガバナンス
テーブル形式は単体では完結しません。ほぼ必ずカタログとセット運用になります。
ここを軽視すると「テーブルの扱いは良いが発見性が弱い」状態になります。
7. 障害復旧の体験
タイムトラベルは「昨日の状態に戻す」以上の価値があります。
- 誤ったバッチのロールバック
- A/Bでの再現
- 監査
Icebergのようにスナップショット中心の思想は、運用の説明性が高い文脈で特に強いです。
Sparkでの作成例
-- Iceberg
CREATE TABLE catalog.db.events_iceberg (
event_id STRING,
user_id STRING,
ts TIMESTAMP,
payload STRING
)
USING iceberg
PARTITIONED BY (days(ts));
-- Delta
CREATE TABLE db.events_delta (
event_id STRING,
user_id STRING,
ts TIMESTAMP,
payload STRING
)
USING delta
PARTITIONED BY (date(ts));
-- Hudi (Spark SQL拡張が前提の環境を想定)
CREATE TABLE db.events_hudi (
event_id STRING,
user_id STRING,
ts TIMESTAMP,
payload STRING
)
USING hudi
TBLPROPERTIES (
primaryKey = 'event_id',
preCombineField = 'ts',
type = 'COPY_ON_WRITE'
);
ポイントは「SQLで作れる」ことより、後続の運用ジョブがどうなるかです。
特にHudiは主キーやpreCombineFieldの設計が品質とコストに直結します。
ありがちな失敗パターン
失敗1: 追記前提で始めたが、半年後に更新要件が爆発
対策: 最初から「訂正が入るか」を業務側に確認し、1%でも可能性があるなら増分更新戦略を用意しておく
失敗2: パーティションを早々に固定してしまう
対策: 変更前提で設計し、変更時の手順とコスト見積もりをドキュメント化
失敗3: 小ファイル対策が後手に回る
対策: 早期に以下を決めておく
- 目標ファイルサイズ
- コンパクション頻度
- 監視指標
失敗4: カタログや権限が後回し
対策: テーブル形式の採用と同時に以下をセットで導入
- データ発見性
- スキーマ変更フロー
- 利用者へのアナウンス
私ならこう選びます
| シナリオ | 選択 | 理由 |
|---|---|---|
| BIと分析が中心で、Trinoや複数エンジンも使う | Iceberg | 隠蔽パーティショニングと進化前提の仕様が運用コストを下げやすい |
| SparkとDatabricks中心で、チームの学習負荷を抑えたい | Delta | SparkにACIDを持ち込むストレージレイヤとしての立ち位置が明快 |
| イベントの訂正や状態更新が日常茶飯事 | Hudi | 更新と増分処理の思想が最初から強い。継続的に機能改善も進む |
まとめ
オープンテーブル形式の選定は、宗教戦争に見えて実はかなり現実的です。
- 分析主導のマルチエンジン → Iceberg
- Spark中心の統一運用 → Delta
- 更新とCDCが主役 → Hudi
そしてどれを選んでも、重要なのは以下の3点に集約されます。
- コンパクション運用
- パーティション戦略の寿命管理
- スキーマ変更フロー