代表的な面接トピック

データエンジニアリング面接:Parquet Variant Shredding をどのように設計しますか?

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

質問

データレイクはスキーマ形状が頻繁に変化する JSON イベントを受信しますが、頻繁に使用される共通フィールドは Parquet でカラムプルーニング可能(column-prunable)である必要があります。Variant エンコーディングと Variant Shredding について説明し、取り込み、読み取り、スキーマ進化、およびフォールバックを設計してください。

課題の背景とコンテキスト

データレイクは形状が頻繁に変わる JSON イベントを受信しています。チームは、任意のフィールドを保持しつつ、頻繁にクエリされるフィールドについてはカラム単位での読み取りおよびプルーニングを可能にしたいと考えています。Parquet Variant の value および metadata コンポーネント、Variant Shredding の typedvalue および fieldoffset について説明し、互換性、スキーマ進化、および検証を設計してください。

Apache Parquet の仕様では、Variant はバイナリの value フィールドと metadata フィールドで表現されます。Variant Shredding は、部分的に均質なフィールドを別個のカラムとして抽出し、オフセットによって元の値を再構成できます。この面接では、JSON を単一の不透明な文字列カラムに入れるのではなく、フォーマットの不変条件、読み取りセマンティクス、およびワークロードの実証データをテストします。

面接官が見ているポイント

面接官は、自己記述的なメタデータと Variant 値を分離できるか、そして typedvalue、fieldid、field_offset の関係を説明できるかを確認したいと考えています。欠落フィールド、混在型、フィールド順序、バージョンの進化を処理できること、シュレッディングによってプロジェクション、述語プッシュダウン、圧縮、フォールバックがどのように可能になるかを説明できること、そして同等性、パフォーマンス、互換性のマトリクスを用いて設計を証明できることが求められます。

最初に確認すべき明確化のための質問

フィールドとクエリ

頻繁にクエリされるパス、型の安定性、任意の未知フィールドを保持する必要性、およびクエリエンジンが Variant およびシュレッドされたカラムをサポートしているかを確認します。

互換性とガバナンス

どの旧バージョンのリーダーがファイルを開く必要があるか、スキーマレジストリが存在するか、削除や名前変更がどのように定義されているか、および不正な形式のレコードが生の Variant カラムに入力される可能性があるかを確認します。

パフォーマンス目標

スキャン割合、オブジェクトストレージのリクエストコスト、書き込みレイテンシ、圧縮率、キャッシュ予算、および再構成の CPU 予算を確認します。1 つの JSON サンプルから価値を推測してはなりません。

30秒で答える要約

「私は Variant を value と metadata という 2 つのバイナリコンポーネントとして扱い、metadata にオブジェクトキーや型情報を記述させます。安定していてボリュームの大きいサブフィールドは typedvalue および fieldoffset カラムにシュレッドし、未知のフィールドには生の Variant を保持します。読み取り時は field_id とオフセットによってセマンティクスを再構成し、明示的な null や型の不一致に対応します。展開前には、新旧リーダーのマトリクス、ランダム化されたネストデータの同等性テスト、カラムプルーニングのチェック、および実際のスキャンコスト測定を実施し、不合格の場合はシュレッドされていないカラムにフォールバックします。」

ステップごとの詳細な解答

ステップ 1: Variant の不変条件を定義する

すべてのレコードに対して value と metadata を保存します。metadata は value 内の型、キー、オフセットを説明する必要があり、field_id はファイル内で安定した解釈を持つ必要があります。null、欠落値、配列、オブジェクト、および数値型のエンコーディングを定義します。

ステップ 2: シュレッド対象の候補を選択する

安定した型を持ち、頻繁にクエリされ、測定可能なメリットがあるパスのみを抽出します。疎なパスや高度にポリモーフィックなパスは Variant に残し、低密度のカラム急増や書き込み増幅を回避します。ルールはバージョン管理された設定から適用します。

ステップ 3: typed_value とオフセットを設計する

カラムナ処理に適したパスに対して typedvalue を書き込みます。ネストされた構造に対しては fieldid、field_offset、または同等の位置データを保持し、リーダーが Variant を再構築できるようにします。オブジェクトのフィールド順序にセマンティクスが含まれると仮定してはなりません。

ステップ 4: スキーマの進化を処理する

新しいフィールドはまず Variant に保持し、クエリパターンが安定した後にシュレッディングルールを追加します。型が変更された場合は、物理的なカラム型を暗黙的に変更するのではなく、新しい field_id またはバージョンを作成します。削除後も古いスナップショットを読み取るために必要なメタデータの解釈を保持します。

ステップ 5: 読み取りとプルーニングを計画する

シュレッドされたパスのみを必要とするクエリの場合、typed_value をプロジェクションし、統計情報を活用します。未知のパスの場合は、value と metadata を読み取ります。値が null、欠落、またはポリモーフィックである場合でも、述語プッシュダウンが安全であることを証明する必要があります。

text
read(record, path):
  if path has shredded column:
    value = typed_value[row]
    if value is present: return value
  variant = decode(value[row], metadata[row])
  return lookup_path(variant, path)

ステップ 6: 整合性チェックを追加する

型、配列の順序、欠落フィールド、null セマンティクスを比較しながら、レコード単位で再構成の同等性チェックを実行します。field_offset の境界、メタデータ参照、行グループ(row group)間の読み取りをサンプリングし、不一致がある場合はパブリッシュをブロックします。

ステップ 7: コストとフォールバックを測定する

シュレッド済みのみのクエリ、未知パスのクエリ、完全な再構成について、レイテンシ、スキャンバイト数、オブジェクトストレージリクエスト数、圧縮率、および CPU を個別に測定します。生の Variant を書き込むためのスイッチを保持し、リーダーがサポートしていない場合や測定されたメリットがしきい値に達しない場合は、ファイルバージョンによってフォールバックにルーティングします。

模範解答

私は Variant の value/metadata を完全な信頼できる情報源(Single Source of Truth)として維持し、安定した高ボリュームのパスのみをシュレッドします。typedvalue はカラムに適した値を格納し、fieldid と fieldoffset によってリーダーは仕様に従ってネストされたセマンティクスを再構築できるようにし、未知のフィールドは生の Variant からクエリ可能なままにします。バージョン管理されたルールと新しい fieldid により、物理型を暗黙的に変更することなくスキーマ進化を管理します。リリース前には、新旧リーダー、欠落/null値、ポリモーフィック配列、プルーニングの安全性、再構成の同等性をテストし、スキャンバイト数、リクエスト数、CPU に基づいて機能を有効化します。

よくある間違い

  • 間違い: Variant を 1 つの JSON 文字列カラムとして扱う。 → 失敗の理由: 自己記述的なメタデータとカラムナ抽出のメリットが失われます。 → 修正方法: value、metadata、field_id、オフセットの役割を明記します。
  • 間違い: すべてのパスをシュレッドする。 → 失敗の理由: 疎なパスによってカラムの急増と書き込みの増幅が発生します。 → 修正方法: クエリ頻度、型の安定性、データ密度に基づいてパスを選択します。
  • 間違い: field_id をフィールドの順序で置き換える。 → 失敗の理由: オブジェクトの順序変更によって意味が変わってはなりません。 → 修正方法: 指定された識別子とオフセットを使用して再構成します。
  • 間違い: クエリ結果のみを比較し、古いリーダーのテストをスキップする。 → 失敗の理由: フォーマットサポートやフォールバックのリスクは本番環境で発生します。 → 修正方法: ファイルバージョン、リーダーバージョン、機能の互換性マトリクスを構築します。

フォローアップの質問と回答

シュレッディングを避けるべきなのはどのような場合ですか?

パスが極めて疎である場合、型が絶えず変化する場合、クエリが稀である場合、またはリーダーがサポートを欠いている場合は、Variant をそのまま維持します。測定されたスキャンとしきい値に基づいて判断します。

安全でない述語プルーニングをどのように防ぎますか?

統計情報がパスをカバーし、欠落、null、型の不一致を明確に区別できる場合にのみ述語をプッシュダウンします。それ以外の場合は、候補行を読み取って Variant を解釈します。

再構成の同等性をどのようにテストしますか?

ネストされたオブジェクト、配列、重複キー、null、欠落フィールド、および複数の数値型を生成します。ファイルバージョン間で正規化された元の Variant と再構成された Variant を比較します。

古いリーダーが Variant を読み取れない場合はどうしますか?

ファイルの対応機能に基づいて、互換性のある書き込みフォーマットまたはサイドカー変換サービスにルーティングします。サポートされていないエンコーディングエラーを空の結果に変換してはなりません。フォールバックは移行が完了した後にのみ削除します。

公開情報ソース

関連する質問