レイクハウス
オープンテーブルフォーマットストレージとSparkコードに関するリファレンスグレードの意思決定支援です。バージョンと日付の情報は references/landscape-2026-11.md にあります。バージョン固有の主張をする前に必ず確認してください。Databricks、Snowflake、AWSのサービス固有の情報については、de-vendors スキルとそれが示す公式ベンダースキルを使用してください。
1. レイクハウスが必要か?
最も安価な選択肢から始めて、記載されているトリガーが実際に発生し、測定された場合にのみ上位のオプションに移行してください。
- Postgres(または単一ファイルセット上のDuckDB):データが約1TB未満で、1つのエンジンが読み取り、同時に分析するユーザーが少ない場合。理由:カタログ、コンパクション、実行クラスターが不要。
- クラウドウェアハウス:多くのSQLユーザーが弾力性のある同時実行性を必要とし、同じデータに別のエンジンがアクセスする必要がない場合。理由:マネージドストレージ、メンテナンス、ガバナンスが価格に含まれている。
- レイクハウス:次の条件の少なくとも1つが当てはまる場合:数十TB以上;複数のエンジン(Spark、Trino、DuckDB、Python)が1つのコピーを読み取る必要がある;MLやファイルベースのワークロードがSQLとテーブルを共有する;オブジェクトストレージのストレージコストが、そのボリュームでのウェアハウスストレージよりも明らかに低い;オープンフォーマットが必須要件(移行、監査、ベンダーニュートラル)。
- 数値のない「将来のスケール」のためにレイクハウスを選択しないでください。推奨する前に、行数、日々の増加量、同時読み取り数、エンジンを確認してください。
- オープンフォーマットを希望する小規模および中規模データの場合:DuckLake、またはプレーンなParquetとDuckDBの組み合わせが、Sparkを実行するよりも優れています。
2. Parquetレイアウト
- ファイルあたり128MB~1GBを目標とし、不明な場合は256MB~512MBを目指してください。32MB未満のファイルが多いと、リスト取得とオープンのコストがクエリの大部分を占めます。
- ファイルあたり約128MBの非圧縮行グループ(100万行以上)。小さいグループは最小値/最大値のプルーニングと圧縮を弱めます。
- クエリがフィルタするカーディナリティの低い列(日付や月、地域)でのみパーティショニングしてください。適切なパーティションは、ライターバッチごとに少なくとも1つの目標サイズのファイルを保持します。パーティションの平均が1GB未満の場合は、より粗い粒度またはクラスタリングを使用してください。
- 高カーディナリティのIDでパーティショニングしないでください。それらの列にはクラスタリング(Delta Liquid Clustering、Iceberg ソート順 / Z-order)を使用してください。
- 最も頻繁にフィルタされる列でファイル内をソートまたはクラスタリングし、最小値/最大値の統計情報が行グループをプルーニングできるようにします。
- ストリーミングや頻繁なマイクロバッチ書き込みは小さなファイルを作成します。コンパクションをスケジュールし、ライターがファイルサイズを調整するのに依存しないでください。
3. テーブルフォーマットの選択
機能リストではなく、既存のエンジンとカタログに基づいて決定してください。
- Delta Lake:DatabricksまたはSparkがプライマリエンジンである場合、またはUnity Catalogがすでにガバナンスレイヤーである場合に選択します。最も強力なSpark統合を提供します。他のエンジンは読み取り可能ですが、書き込みサポートは異なります。
- Apache Iceberg:複数のエンジンが書き込む必要がある場合、またはベンダーニュートラルなカタログ(REST)を希望する場合に選択します。v3では、削除ベクトル、行の系統、バリアント型が追加されています。テーブルをアップグレードする前に、パス上のすべてのエンジンとカタログが使用するv3機能をサポートしていることを確認してください。古いリーダーは新しいフォーマットバージョンで失敗するためです。
- DuckLake:小規模から中規模のデータ、単一チーム、そしてすでに運用しているSQLデータベースをカタログとして使用する場合に選択します(メタデータはプレーンテーブルに格納されます)。実行コストが低く、エコシステムは若いです。Spark、Trino、Snowflakeが同じテーブルに書き込む必要がある場合は選択しないでください。
- 相互運用性レイヤー(Delta UniForm、DeltaテーブルのIcebergメタデータ)は読み取りパスです。テーブルごとに1つのフォーマットをライターオブレコードとして選択してください。
- 後でフォーマットを変更するには、再書き込みまたはメタデータ変換が必要です。ライターオブレコードは慎重に選択してください。
4. カタログ
- カタログはコミットコーディネーターであり、アクセス制御ポイントです。フォーマットの詳細を気にする前にカタログを選択してください。
- Unity Catalog:Databricks中心のデフォルト。DeltaおよびIcebergのガバナンスを提供します。
- AWS Glue and S3 Tables:スタックがAWSネイティブの場合に選択します。S3 Tablesはコンパクションを管理しますが、AWSにロックインされます。
- Iceberg RESTカタログ(例:Polaris):マルチエンジン、マルチクラウドのニュートラル性のために選択します。
- DuckLake:カタログはSQLデータベースです。マルチユーザー使用の場合はPostgresでバックアップし、1つのライターのみの場合はSQLiteまたはDuckDBを使用します。
- 2つのカタログに同じテーブルパスを登録させないでください。一方へのコミットが他方のビューを破損します。
5. メンテナンス(スケジュールしないとテーブルは劣化します)
- 小さなファイルのコンパクション:Deltaの
OPTIMIZE、Icebergのrewrite_data_files、DuckLakeの隣接ファイルマージ。大量取り込み後に実行します。書き込みごとではありません。 - 古いデータの削除:Deltaの
VACUUM、Icebergのexpire_snapshotsと孤立ファイル削除、DuckLakeのスナップショット有効期限とクリーンアップ。保持期間は、最長のリーダーまたはタイムトラベルウィンドウよりも長くなければなりません。実行中のリーダーの下でファイルを削除すると、リーダーが破損するためです。 - クラスタリングまたはZ-orderは、フィルターに表示される列に対してのみ行います。再クラスタリングはデータを書き換え、計算コストがかかります。
- メンテナンスの前後でファイル数と平均ファイルサイズを確認してください。どちらも変わらないメンテナンスジョブは何もしていません。
- エンジンが提供する場合は、まずVACUUMまたは孤立ファイル削除をドライランで実行してください。
6. MERGE、スキーマ進化、タイムトラベル
- MERGE:まずソースを一致キーで重複排除してください(重複するソースマッチは失敗するか、二重適用されます)。ON句にパーティションまたはクラスタリング述語を含めて、エンジンがテーブル全体をスキャンする代わりにファイルをプルーニングできるようにします。
- 大規模テーブルでは、完全なテーブル全体のMERGEを繰り返すよりも、追加と定期的なMERGE、またはべき等キーを優先してください。
- スキーマ進化:追加列は安全です。名前変更と型拡大は、フォーマットとバージョンに依存します(Deltaのカラムマッピング、IcebergのフィールドID)。名前変更を行う前に、重要なリーダーがサポートしているか確認してください。制御できない本番パイプライン入力で自動スキーママージを有効にしないでください。
- タイムトラベルは、デバッグ、監査、ロールバックのためのツールであり、バックアップではありません。保持しているファイルのコストがかかります。保持期間が長いほど、ストレージが増え、メタデータが遅くなります。保持期間は意図的に設定し、重要なテーブルには実際のバックアップを保持してください。
7. PySpark 4.x の基本
- 変換は遅延実行されます。アクションが実行されるまで何も実行されません。1つの計画を構築し、1つの書き込みをトリガーします。
count()やshow()を繰り返すと、キャッシュしない限り計画全体が再実行されます。 - 大規模データでの
collect()、toPandas()、.rddを避けてください。すべてがドライバーにプルされます。最初に制限または集計してください。 - Python UDFよりも組み込み関数やSQL式を使用してください。UDFはオプティマイザーのプッシュダウンをブロックし、行をシリアライズします。やむを得ない場合は、ベクトル化(Arrow)UDFを使用してください。
- ワイド操作(join、groupBy、window、distinct)はシャッフルを引き起こします。これらの前にフィルターとプロジェクションを行い、
explain()で計画を確認してください。 - 結合の小さな側をブロードキャストする(
broadcast(df))のは、エグゼキュータメモリに収まる場合のみにしてください。無制限のテーブルをブロードキャストしないでください。 - アダプティブクエリ実行(AQE)はSpark 4.xでデフォルトでオンになっており、パーティションの結合、スキュー結合、結合戦略の切り替えを処理します。計画を読んだ後にチューニングし、その前には行わないでください。
- スキューは1つの遅いタスクとして現れます。AQEが不十分であることが証明された場合にのみ、ホットキーをソルト化または分離してください。
- Spark Connectはクライアントサーバーモードです。コードはリモートセッション上のシンクライアントとして実行されます。Connectコードでは、ドライバローカルのJVM内部(
sparkContext、RDD API)に依存しないでください。 - 推測されたパーティション数で再パーティションするのではなく、コンパクションを介して明示的なターゲットファイルサイズで書き込んでください。
8. ローカルファーストパス
クラスターに費用を支払う前に、DuckDBまたはpolarsで開発およびテストを行ってください。
- DuckDBは拡張機能を介してDeltaとIcebergを読み取り、DuckLakeカタログをアタッチします。polarsは述語プッシュダウンを使用してDeltaとIcebergを遅延読み取りします。
- 実際のテーブルレイアウトに対してサンプルまたは1つのパーティションを使用して、ファイルプルーニングの動作が一致するようにしてください。
- 実際のエンジンで書き込みセマンティクス(MERGE、削除ベクトル、v3機能)を確認してください。ローカルリーダーはフォーマット機能に遅れる場合があります。ローカル読み取りの成功は必要条件であり、十分条件ではないことを認識してください。
手順
- 数値を取得します:サイズ、増加量、ライター、リーダー、エンジン。 2. セクション1を進み、最も安価に適合するオプションで止まります。 3. レイクハウスの場合:カタログを選択し、次にフォーマットを選択します(セクション4、3)。 4. 最初のロードの前にレイアウトとメンテナンススケジュールを設定します。 5. ローカルでプロトタイプを作成し(セクション8)、
explain()出力を確認してからクラスターで実行します。 6. ファイル数とクエリ計画で検証し、仮定に頼らないでください。
既知の落とし穴と修正:references/gotchas.md を参照してください。