代表的な面接トピック

データエンジニアリング面接:選択的スキャンにParquetのPage Indexをどう活用するか?

データ難しい
Offer.cc 編集チーム公開日 更新日

質問

毎日40 TiBのParquetテーブルが書き込まれています。クエリは通常1%未満の行に一致しますが、スキャンバイト数はテーブル全体に近いままです。Page Indexをどのように評価し、有効化しますか?インデックス構造、ソート順の前提、読み取り/書き込みコスト、古いリーダーとの互換性、および受け入れ基準について説明してください。

課題とスコープ

テーブルはParquet形式で毎日書き込まれており、各row group内には多数のデータページが存在します。クエリは一般的にcustomer_idと時間範囲でフィルタリングを行い、テーブルのほぼ全体をスキャンしながら1%未満の行に一致します。古いリーダーの正確性を維持し、メタデータコストを抑制し、キャッシュやリソースの変更ではなくページプルーニングによってパフォーマンス向上がもたらされたことを証明しながら、オプションのPage Indexによって無関係なページの読み取りをどのように削減できるかを説明してください。

容量、選択性、スキャン率は面接上の前提条件であり、普遍的なベンチマークではありません。この質問は、データエンジニアリング、レイクハウスエンジン、クエリ最適化、ストレージインフラストラクチャの職種に適しています。その核となるスキルはカラム型ファイルレイアウトと述語プッシュダウン(predicate pushdown)であり、dataに属します。

面接官が評価するポイント

第一に、ColumnIndexとOffsetIndexを区別できるか?前者はページごとの境界統計情報を使用してどのページが一致する可能性があるかを判断し、後者は一致する行範囲を射影されるカラムのオフセットにマッピングします。

第二に、順序付けされた(ordered)カラムと順序付けされていない(unordered)カラムの違いを説明できるか?順序付けされたカラムは境界の二分探索を利用できますが、順序付けされていないカラムは多くの場合、ページ境界を順次チェックする必要があります。Page Indexは一般的なセカンダリインデックスではありません。

第三に、正確性を保証できるか?切り詰められた(truncated)min/max値は候補セットを拡大させる可能性がありますが、一致する可能性のあるページを除外してはなりません。Null値、NaN、カラム順序、およびcolumn_ordersはフォーマットの定義に従います。

第四に、トレードオフを定量化できるか?インデックスのメタデータはフッター領域のI/Oと書き込み処理を増加させますが、選択的スキャンはデータページのI/Oを削減できます。一定の高速化を約束するのではなく、実際のワークロードで測定してください。

第五に、フォールバックを提供できるか?古いリーダーはPage Indexを無視し、通常のrow-groupまたはページ統計情報を使用して正確に読み取ることができます。インデックスを有効にしても、結果のセマンティクスを変更してはなりません。

最初に明確にすべき質問

  • エンジンとリーダーはColumnIndexとOffsetIndexを実装しており、デフォルトでそれらを読み取りますか?
  • customer_idは書き込み時にレンジクラスタ化(range-clustered)またはソートされていますか、それとも順序付けされていませんか?
  • 述語は完全一致、範囲、前方一致、それとも複雑な式ですか?
  • 既存のファイルにページレベルの統計情報が含まれていますか?また、エンコーディングとページサイズは何ですか?
  • 古いリーダーの最小バージョンとクロスランゲージ互換性マトリクスはどうなっていますか?
  • 最適化の対象はポイントルックアップ、範囲スキャン、それともテーブル全体の集計ですか?

30秒の回答フレームワーク

「まずリーダーのサポートを確認し、ファイルフッターをサンプリングしてページ数、ColumnIndexサイズ、ソート順、述語の選択性を確認します。ソートされたcustomer_idについては、ページのmin/max境界を使用して候補を特定します。その他のカラムについては、境界をテストし、OffsetIndexを使用して一致する行を射影カラムにマッピングします。Page Indexはセカンダリインデックスではないため、ベンチマークで価値が示された場合にのみソート順やページサイズを変更します。古いリーダーはこれを無視しても同じ結果を返す必要があります。最後に、コールドキャッシュかつ固定リソースの環境下で、インデックス無効化のコントロール群と比較してスキャンバイト数、読み取りページ数、プランニング時間、エンドツーエンドのp95、フッター/インデックスオーバーヘッドを比較します。」

ステップごとの回答

ステップ1: フォーマットとリーダーのサポートを確認する

Page Indexは、ColumnIndexおよびOffsetIndexを含むオプションのColumnChunkメタデータです。ファイルのメタデータを検査して、インデックスの場所と長さ、カラム順序、column_ordersを確認し、対象エンジンで明示的なページプルーニングメトリクスを有効にします。リーダーがインデックスの書き込みのみを行い、それを使用しない場合、書き込みを行ってもスキャンは削減されません。

text
for each row_group:
  read ColumnIndex for predicate columns
  select pages whose min/max may match predicate
  use OffsetIndex to map selected row ranges to projected columns
  read only those page ranges

ステップ2: 順序付けされたカラムと順序付けされていないカラムを分ける

Parquetのドキュメントには、順序付けされたカラムの境界は二分探索をサポートする一方、順序付けされていないカラムは通常、順次min/maxチェックが必要であると記載されています。順序付けはフォーマット全体の要件ではありません。テーブルのカーディナリティのみを使用するのではなく、row groupごとの値範囲の重複を記録します。

ステップ3: min/maxを保守的に解釈する

ライターは長い文字列を切り詰めたり、実際の値範囲をカバーする境界を使用したりする場合があります。このような境界は追加の候補ページを生じさせる可能性がありますが、一致する可能性のあるページを除外してはなりません。column_ordersに従ってNull値、NaN、比較を解釈します。統計情報が不完全な場合は、安全にページを読み取ります。

ステップ4: カラム間で読み取りを連携させる

ColumnIndexは述語カラムに対してのみ候補ページを特定します。射影には他のカラムも必要なため、OffsetIndexが一致する行範囲をそのページオフセットにマッピングします。ページ境界はカラム間で異なる場合があるため、あるカラムのページ番号を別のカラムに再利用してはなりません。OffsetIndexがない場合、リーダーはより多くのカラムを順次デコードする可能性があります。

ステップ5: 書き込みとメタデータのコストを測定する

ページ数が増えるとページヘッダーとインデックスエントリが増加し、ページサイズが大きくなるとプルーニングの粒度が低下します。クエリ選択性、行幅、圧縮、ページサイズのマトリクスでベンチマークを実施します。ポイントルックアップではより多くのメタデータが正当化される場合がありますが、広範なスキャンや完全な集計では追加のフッターI/Oが発生するだけになる可能性があります。

ステップ6: 互換性とロールアウトを設計する

書き込みを有効にする前に、すべてのコンシューマーを棚卸しします。Page Indexを無視する古いリーダーは、row-group統計情報または通常のページ読み取りを使用し、同じ結果を返す必要があります。インデックスを無効にしたコントロール群を維持しながら、まずは新しいファイルおよび固定されたパーティションにロールアウトします。サポートしているリーダーとサポートしていないリーダーについて、結果ハッシュ、スキャンバイト数、エラーを記録します。

ステップ7: 再現可能な受け入れ基準を定義する

コールドキャッシュ、固定並行性、反復試行を用いて、同じスナップショット上で同等のクエリを実行します。スキャンバイト数、読み取りページ数、スキップ率、フッター/インデックスバイト数、デコードCPU、エンドツーエンドのレイテンシ、および結果の検証を記録します。重複の多い順序付けされていないパーティションをネガティブコントロールとして維持し、選択性の低いワークロードでインデックスがコストを増やすだけの場合はロールアウトを停止します。

モデルアンサー

「まずリーダーがColumnIndexおよびOffsetIndexを使用することを確認し、フッターをサンプリングしてページ数、インデックス長、column_orders、書き込みのソート順を把握します。Page Indexはオプションのメタデータであり、セカンダリインデックスではありません。どのページが一致する可能性があるかを示します。

ソートされたcustomer_idについては、ページのmin/max境界を二分探索します。順序付けされていないカラムについては、境界を順次チェックします。OffsetIndexを使用して述語に一致する行を射影カラムにマッピングし、あるカラムのページ番号を別のカラムに再利用することは決してしません。切り詰められた統計情報は候補を広げる可能性があります。統計情報の欠落、Null値、または不確実な順序の場合は安全な読み取りが必要です。

書き込み時には、選択性、ページサイズ、圧縮、フッターの増大をベンチマークします。ロールアウト中は古いリーダーおよびインデックス無効化のコントロール群を保持し、同一の結果を要求します。コールドキャッシュと固定リソースの下で、展開前にスキャンバイト数、スキップ率、インデックスI/O、CPU、p95、結果ハッシュを比較します。」

よくある間違い

  • Page Indexをセカンダリインデックスとして扱う → 順序付けされていないカラムには依然として多くの候補が存在する可能性がある → 値範囲の重複と選択性を測定する。
  • ColumnIndexのみを書き込む → 射影カラムが一致する行ごとにジャンプできない → OffsetIndexマッピングを検証する。
  • 切り詰められたmin/maxを正確なものとして扱う → 実際の一致が除外される可能性がある → 保守的な候補拡張のみを許可する。
  • ウォームキャッシュでのみテストする → キャッシュがI/Oを隠蔽する → コールドキャッシュでのコントロール実行を繰り返す。
  • カラム間でページ番号を再利用する → ページ境界が異なる → 行範囲とオフセットを使用する。
  • 古いリーダーを無視する → ロールアウトによって互換性のリグレッションが発生する → リーダーマトリクスとフォールバックを維持する。
  • 結果を確認せずにレイテンシのみをチェックする → プルーニングのバグにより行が失われる可能性がある → ハッシュとビジネス集計を比較する。
  • デフォルトですべての場所で有効にする → 選択性の低いスキャンでメタデータコストが発生する → テーブルまたはパーティションごとにロールアウトする。

フォローアップの質問

フォローアップ 1: なぜ順序付けされたカラムの方がメリットが大きいのですか?

ソートによって値が隣接するページに集中するため、範囲が二分探索をサポートする連続したページ区間にマッピングされることが多くなります。順序付けされていない値は重複が多くなり、より多くの候補が生成されます。最終的な結果は依然としてページサイズと述語の選択性によって決まります。

フォローアップ 2: OffsetIndexなしでページをスキップできますか?

述語カラムは候補を特定できますが、射影カラムは同じ行範囲を直接特定できないため、リーダーはより多くの順次読み取りを必要とする場合があります。ファイルメタデータのみからサポートを推測するのではなく、対象のリーダーを検証してください。

フォローアップ 3: 切り詰められた統計情報は安全ですか?

正しいライターは、実際の範囲をカバーする保守的な境界を提示します。これによりフォールスポジティブ(偽陽性)や余分な読み取りが発生する可能性はありますが、フォールスネガティブ(偽陰性)は発生しません。その保証が得られない場合は、通常の読み取りにフォールバックします。

フォローアップ 4: 行が失われていないことをどのように証明しますか?

Page Indexを有効にした場合と無効にした場合で同じスナップショットを実行し、完全な結果、カウント、集計、主要サンプルを比較し、合成データを使用して境界値、Null値、重複、長い文字列をテストします。

フォローアップ 5: Page Indexを書き込む価値がないのはどのような場合ですか?

フルスキャン、選択性の低い述語、ページ数が少ない場合、またはフッターがレイテンシの大部分を占める場合は、メリットが得られない可能性があります。インデックスバイト数、書き込みCPU、メンテナンスコストを比較し、テーブルごとまたはパーティションごとのスイッチを維持してください。

フォローアップ 6: スキーマやソート順の変更についてはどうですか?

新しいファイルは自身のスキーマとcolumn_ordersに基づいて解釈されるべきであり、テーブル全体で単一のグローバルなソート順を前提とすることはできません。混在する履歴ファイルには機能に応じた処理と、欠落しているインデックスの監視が必要です。

公開情報ソース

関連する質問