質問とシナリオ
Icebergテーブル向けのベクトル検索拡張を設計してください。データファイルが行データを保持し、クエリエンジンはスナップショットを読み取ります。ベクトルインデックスはPuffinサイドカーファイルに格納され、スナップショットメタデータによって参照されます。この設計は、スナップショットの分離性、タイムトラベル、データファイルのメンテナンスを損なうことなく、近似最近傍クエリをサポートする必要があります。
日次のバッチ追記と少量の更新を想定してください。近似結果は、呼び出し元からインデックスバージョンが可視である場合にのみ許容されます。Apache Puffinファイル形式の機能と、研究で提案されている特定のANNレイアウトを区別してください。実験的なグラフ設計が自動的にIcebergの標準動作になるわけではありません。
面接官が評価しているポイント
スナップショットとインデックスの一貫性
優れた回答では、データスナップショット、Puffin blob、およびインデックスメタデータを1つの可視なコミットでバインドします。インデックスファイルを単にアップロードするだけでは、クエリ可能にはなりません。
近似検索とファイルプルーニング
ベクトルインデックスは候補を取得するものであり、最終的な距離および可視性のチェックは引き続きデータ行を読み取って行うことを説明してください。インデックスの欠損、陳腐化、低再現率(low-recall)を明示的に処理します。
増分メンテナンスと削除
追記、更新、削除、マージ、コンパクションを網羅します。単発のオフライン構築だけで終わらせてはいけません。
コンピュート・ストレージ分離環境での運用性
オブジェクトストレージがPuffinをどのように保持し、コーディネーターがシャードをどのようにスケジュールし、ガベージがどのように制限され、鮮度とフォールバックがどのように監視されるかを述べてください。
回答前の明確化のための質問
- 埋め込みの次元数、距離関数、クエリレイテンシ、および最小再現率はどれくらいですか?
- クエリは最新のスナップショットを使用する必要がありますか?それとも制限付きのインデックス遅延は許容されますか?
- 更新や削除は、追記専用のCDC、Icebergのequality deletes、またはデータファイルの書き換えのどれですか?
- 単一のエンジンがインデックスを構築しますか?それともSpark、Flink、Trinoで共有する必要がありますか?
- ベクトルは機密データですか?またアクセス制御と暗号化は誰が管理しますか?
- タイムトラベルクエリは履歴インデックスを再利用する必要がありますか?それとも現在のスナップショットに対するインデックスのみが必要ですか?
30秒の回答フレームワーク
「Icebergデータファイル、Puffinインデックスblob、スナップショットメタデータを分離して保持しつつ、単一のスナップショットIDに対する1回のコミットでそのバインディングを公開します。クエリは可視なスナップショットを選択し、そのインデックス参照を読み取り、ANN候補取得を実行した後、データファイルから行バージョンと正確な距離を検証します。インデックスが存在しない場合や陳腐化している場合は、パーティションまたはファイルのフルスキャンにフォールバックし、品質ステータスを公開します。追記はデルタインデックスを作成でき、更新や削除はトゥームストーンや削除レイヤーによってフィルタリングされ、コンパクションによってベースラインが再構築されます。インデックスジョブは楽観的スナップショットコミットを使用し、鮮度、再現率サンプル、フォールバック率、Puffinガベージを監視します。」
ステップごとの詳細な回答
ステップ1: データとインデックスの境界を見積もる
説明のための仮定として、768次元のfloat32ベクトルが10億件ある場合、10億 × 768 × 4バイトで、カラムナ圧縮やインデックスオーバーヘッドを考慮する前の生ベクトルだけで約3TBが必要です。この見積もりにより、インデックスを単一のマニフェストやコーディネーターのメモリに配置することは除外されます。オブジェクトストレージ内でシャード化し、クエリパーティションごとにロードする必要があります。
ステップ2: Puffinをスナップショットにバインドする
Puffinは、Icebergマニフェストが直接保持できないインデックスや統計のblobを格納します。各blobには、タイプ、フィールド、パーティション、データファイル参照などのメタデータが含まれます。インデックスビルダーはPuffinを書き込み、インデックスの場所、バージョン、カバレッジをsummaryに記録した新しいIcebergスナップショットをコミットします。リーダーは、そのスナップショットで可視になっている場合にのみ参照を受け入れます。
snapshot S42
data files: D100, D101
summary:
vector.index.version = v7
vector.index.puffin = s3://table/metadata/puffin-v7
vector.index.covers = D100,D101ステップ3: クエリパスを設計する
現在のブランチまたはタイムトラベルリクエストをスナップショットSに解決し、カバレッジに基づいてPuffin blobを選択します。ANNグラフまたはシャードは候補行識別子と近似距離を返します。エンジンはそれらのデータファイルを読み取り、S内での行の可視性をチェックし、認可と述語を適用して、正確な距離を再計算します。呼び出し元が鮮度を判断できるように、snapshotidとindexversionを返します。
ステップ4: 追記、更新、削除を処理する
追記ではデルタPuffinインデックスを書き出し、同一のスナップショットコミットでそのファイルを宣言できます。再構築の前段階では、更新や削除はequality deletes、position deletes、またはデルタトゥームストーンを使用して古い候補をフィルタリングします。クエリが削除済み行を返してはなりません。バックグラウンドでデルタインデックスを新しいベースラインにマージし、新しいスナップショットバインディングをアトミックに公開します。障害発生時は、古いインデックスとフォールバックパスを維持します。
ステップ5: 並行コミットとコンパクションを処理する
インデックスジョブはベースラインS42を読み取り、v7を構築します。データコミットによってテーブルがS43に進んだ場合、楽観的並行性制御により、リトライするか、デルタをマージするか、v7を破棄するかを決定します。コンパクションによってデータファイルのパスが変更された場合、古いインデックスは新しいファイルのカバレッジを主張できません。書き換えられたファイルセットにバインドされた新しいblobを構築し、参照追跡と猶予期間の経過後に古いblobをガベージコレクションします。
ステップ6: 運用、分離、フォールバック
オブジェクトストレージがPuffinを保持し、コーディネーターがパーティションまたはファイルセットごとに構築をスケジュールし、クエリノードは小さなルーティング構造やセントロイド構造をキャッシュします。blobの欠損、非互換バージョン、認可エラー、または低い再現率サンプルが発生した場合は、ファイルスキャンへのフォールバック、または検証済み候補と理由のみを含むレスポンスをトリガーします。インデックスの鮮度、構築遅延、クエリのp95、再現率サンプル、Puffinのバイト数、フォールバック率、未参照blobを追跡します。
高品質な回答例
「ベクトル列はIcebergデータファイルに保持し、ANN構造はPuffinに書き出し、その参照をスナップショットの一部として公開します。10億件の768次元float32ベクトルの場合、生ベクトルだけで約3TBになるため、インデックスはコーディネーターで保持するのではなくオブジェクトストレージでシャード化する必要があります。
ビルダーはS42を読み取り、D100およびD101をカバーするPuffin v7を作成し、楽観的コミットを使用して新しいスナップショットを公開します。クエリはタイムトラベルリクエストのスナップショットを固定し、そのスナップショットで宣言されたインデックスのみを読み取り、データファイルを読み取ることで候補の可視性、認可、正確な距離を検証します。インデックスが陳腐化している間、追記はデルタインデックスを使用し、更新や削除は削除ファイルやトゥームストーンによってフィルタリングされ、その後のコンパクションでベースラインが再構築されます。
テーブルがS43に進んでいた場合、リトライするか明示的な陳腐化マーカーを付けない限り、v7を最新とは呼べません。blobの欠損、非互換フォーマット、または低い再現率はスキャンフォールバックをトリガーし、メトリクスにsnapshotid、indexversion、および理由を記録します。古いPuffinは、過去のスナップショットやブランチから一切参照されなくなった後にのみ回収します。」
よくある間違い
- Puffinを新しいプライマリテーブルフォーマットとして扱う → クエリがインデックスの対象データバージョンを証明できない → カバレッジをスナップショットにバインドする。
- アップロードしたインデックスを即座に読み取り可能にする → データファイルとインデックスが異なるスナップショットに属する可能性がある → 単一の楽観的コミットで参照を公開する。
- ANNの候補をそのまま返す → 削除、権限、距離誤差により誤った行が漏洩する → 可視性を再チェックし正確な距離を再計算する。
- 更新後も古いグラフを使い続ける → 削除済み行が取得される恐れがある → 削除レイヤーでフィルタリングし、デルタをマージして、ベースラインを再構築する。
- コンパクション後に古いファイルパスを再利用する → インデックスが存在しないファイルをカバーしていると主張することになる → 書き換えられたセット用に新しいblobとスナップショットを構築する。
- 研究段階のグラフレイアウトをPuffinの標準仕様として扱う → エンジン間の相互運用性が損なわれる → Puffinはストレージとメタデータの用途にとどめ、グラフアルゴリズムは置き換え可能にする。
- インデックス欠損時に全クエリを失敗させる → 新しいパーティションの展開中にサービスが利用不可になる → スキャンにフォールバックし、鮮度と理由を公開する。
- 参照追跡なしでPuffinを削除する → タイムトラベルやブランチの読み取りが破損する → すべてのスナップショット、ブランチ、猶予期間が解放されるのを待つ。
フォローアップの質問と回答
フォローアップ1: タイムトラベルで適切なインデックスをどのように保証しますか?
対応するスナップショットのsummaryに、ファイルカバレッジとバージョンとともに参照を格納します。まずスナップショットを固定し、それ以降のファイルや異なるファイルをカバーするblobを拒否し、存在しない場合は最新のインデックスを暗黙的に使用するのではなくスキャンを実行します。
フォローアップ2: 大量の更新ストリームによってデルタインデックスが無制限に作成されますか?
パーティションまたはファイルセットごとにデルタレイヤーの制限を設定し、それを超えた場合にマージ再構築をスケジュールします。マージ後に新しいスナップショットへアトミックに切り替え、過去のスナップショットから参照されなくなるまで古いデルタを保持します。
フォローアップ3: 2つのエンジンがそれぞれのANNインデックスを相互利用できますか?
blobタイプ、距離関数、ベクトルエンコーディング、行識別子、およびバージョンプロトコルに互換性がある場合にのみ可能です。Puffinはコンテナとメタデータの境界を定義するため、グラフには機能宣言が必要です。そうでない場合は無視してフォールバックします。
フォローアップ4: テーブル全体をスキャンせずに再現率をどのように測定しますか?
本番クエリのごく一部をサンプリングし、厳密な探索または信頼できるベースラインを使用してオフラインで近似的なグラウンドトゥルースを計算します。パーティション、ベクトルバージョン、距離関数ごとにrecall@kを比較します。p95だけを監視するのではなく、サンプルがしきい値を下回った場合にインデックスを陳腐化(stale)とマークします。
フォローアップ5: Puffinファイルまたはオブジェクトストアが一時的に利用できない場合はどうしますか?
blobのチェックサムとメタデータを検証し、レプリカからリトライした上で、タイムアウト後は明示的なステータスを返してスキャンを行うか近似探索を拒絶します。破損したblobを参照する新しいスナップショットは絶対に公開しないでください。
情報源1: Apache Puffin 仕様
Puffinの仕様は、blobメタデータやデータファイル参照を含め、Icebergマニフェストに直接格納できないインデックスや統計のためのファイル形式を定義しています。これは、本回答におけるサイドカー、カバレッジ、スナップショット境界の設計を裏付けています。
情報源2: Puffinを活用したベクトルインデックスの研究
2026年の論文では、Icebergスナップショットに近似最近傍構造を関連付けることを提案しており、コンピュート・ストレージ分離、スナップショットレベルのインデックス管理、および数十億ベクトル環境について議論されています。本回答ではこれを置き換え可能な研究実装として扱い、削除、フォールバック、並行コミット境界を追加しています。
情報源3: データエンジニアリング面接準備ガイド
公開されているデータエンジニアリングガイドでは、SQL、データモデリング、パイプライン、バッチおよびストリーミングシステム、信頼性が重視されています。本回答ではそれらの評価シグナルを、スナップショットの一貫性、インデックスのメンテナンス、シャード化、検証、および障害時のフォールバックに対応させています。