代表的な面接トピック

データエンジニアリング面接:DAGベースのクエリビュー向けキャッシュの設計

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

質問

ある分析プラットフォームには、最大で深さ20の依存関係DAGを形成する500個のマテリアライズドクエリビューが存在します。5分の鮮度ターゲット、部分的な障害、バックフィル、およびホットビューに対応し、毎秒10,000クエリを処理するキャッシュおよびリフレッシュシステムを設計してください。

問題と範囲

各ビューは、ソーステーブルまたは他のビューに対するクエリです。ソースの変更は子孫を無効化しますが、すべての子孫を即座に再計算するのはコストが高すぎます。システムはバージョニングされた結果を提供し、鮮度を公開し、親ビューの互換性のない世代を決して組み合わせてはなりません。リフレッシュ処理は非同期で行うことができ、復旧のために完全な再構築が引き続き利用可能であると仮定します。

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

  • 依存関係のエッジ、世代、無効化、およびトポロジカルなリフレッシュ順序のモデル化。
  • 変更量やクエリの形状に基づいた、増分再計算と完全な再計算の選択。
  • 古い結果、部分的な障害、バックフィル、ホットキー、およびキャッシュの退避(エビクション)の処理。
  • リネージ、マニフェスト、チェックサム、および再生可能なイベントによる正確性の証明。

確認すべき質問

すべてのクエリに鮮度SLOがあるかどうか、結合を増分で維持できるかどうか、更新や削除がどのように届くか、そして読み取り側がエラーよりも古い回答を好むかどうかを尋ねます。ビューに可逆でない集計が含まれている場合、変更された行によってより広範な再計算が必要になることがあります。ビューが追加専用である場合、差分リフレッシュの方が低コストです。

30秒での回答

各ビューに対してバージョニングされたマニフェスト(依存関係の世代、出力場所、行数、チェックサム、および鮮度タイムスタンプ)を保存します。ソースの変更によって無効化イベントが追加され、スケジューラは影響を受ける子孫をトポロジカル順に計算します。クエリがサポートしている場合は差分リフレッシュを使用し、そうでない場合は完全な再構築を使用します。必要なすべての親がターゲットの世代と一致した後にのみ、新しいマニフェストをアトミックに公開します。読み取り側は完全な世代を選択し、ポリシーで許可されている場合は制限付きで古い世代を使用でき、その経過時間を公開します。リプレイ、チェックサム、および定期的な完全再構築によってドリフトを修復します。

ステップごとの詳細解説

1. DAGと世代の表現

各ビューに安定したID、クエリ定義、親ID、および世代を付与します。リフレッシュ計画にはターゲットソースのウォーターマークが含まれ、消費した親の世代が記録されます。リフレッシュの途中で親が変更された場合は公開を拒否し、暗黙的に結果を混ぜ合わせるのではなく、新しいウォーターマークから再試行します。

2. 差分リフレッシュまたは完全リフレッシュの選択

判断基準として、変更量、結合の形状、および集計の可逆性を使用します。エンジンが差分で十分であることを証明できる場合、増分リフレッシュは変更されたパーティションまたは行のみを読み取ります。広範な結合や削除の場合は、完全リフレッシュの方がシンプルです。リフレッシュに失敗しても最後に成功した回答が失われないよう、新しいマニフェストが検証されるまで古い世代を利用可能な状態にしておきます。

3. 無効化のスケジューリングとホットビューの制御

多数のソースイベントを1つのターゲットウォーターマークに合算し、影響を受けるノードを世代ごとに1回処理します。クエリの需要と鮮度の遅れによってビューの優先順位を決定しますが、ホットなアップストリームがコンピュートリソースを使い果たすのを防ぐため、ソースごとの同時実行処理数を制限します。ビューのパラメータと世代ごとに人気のある結果をキャッシュし、個々のキーを個別に削除するのではなく、世代ごとに無効化します。

4. 復旧、バックフィル、および正確性の証明

ワーカーがクラッシュ後に再開できるように、無効化イベントとリフレッシュマニフェストを永続化します。バックフィルは別のターゲット世代の下で実行され、現在の世代と比較した後にのみ公開されます。行数、チェックサム、サンプリングされた集計、およびリネージのウォーターマークを比較し、ビューが5分の鮮度ターゲットを超えた場合や親同士が一致しない場合にアラートを発します。定期的な完全再構築は、増分ドリフトを検出するための正解(オラクル)を提供します。

優れた回答例

ビューごとの鮮度、更新/削除の挙動、および古いデータの読み取りが許容されるかどうかを確認します。各ビューには、親の世代、ソースのウォーターマーク、出力場所、チェックサム、および鮮度を含むマニフェストが存在します。無効化イベントがスケジューラに送られ、スケジューラは処理を合算してトポロジカルに子孫をリフレッシュします。増分可能であることが証明されているクエリには差分リフレッシュを使用し、広範な結合や削除には完全な再構築を使用し、新しい世代をアトミックに公開します。読み取り側で世代が混在することはなく、許容範囲内の古い世代を受け取ることも可能です。リプレイ、バックフィル世代、チェックサム、および定期的な完全再構築により、正確性を検証可能にします。

よくある間違い

  • すべての子孫を即座にリフレッシュする → バーストによって重複した処理が発生する → ターゲットウォーターマークごとにイベントを合算する。
  • 唯一の結果をインプレースで上書きする → 失敗したジョブによって読み取り側に不完全なデータが残る → 不変の世代をアトミックに公開する。
  • すべての集計が増分可能であると仮定する → 削除や非可逆な関数によってドリフトが発生する → クエリの形状に基づく判断基準と完全再構築のフォールバックを使用する。
  • 世代のメタデータなしでキャッシュする → 親と子の回答が一致しなくなる可能性がある → キャッシュキーを完全な世代に紐付ける。
  • 1つのホットビューにすべてのワーカーを消費させる → 他の鮮度SLOが失敗する → ソースごとおよびビューごとの同時実行数上限を使用する。
  • 1つの行数を証明として信用する → サイレントなデータ破損が見過ごされる → チェックサム、サンプル、リネージウォーターマーク、および完全再構築を比較する。

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

子の処理を実行中に親ビューがリフレッシュされた場合はどうなりますか?

子は読み取った親の世代を記録します。公開時点でその世代が最新でなくなった場合、子を破棄するか、新しいターゲットウォーターマークに対して再試行します。混在した世代を決して公開してはなりません。

増分ビューとされるものに削除が発生しました。それでも差分リフレッシュを使用できますか?

変更ログとクエリのセマンティクスに、過去の寄与分を差し引くための十分な情報が残っている場合にのみ可能です。そうでない場合は、影響を受けるパーティションを広げるか、完全な再構築をスケジュールし、鮮度のトレードオフを明示します。

バックフィルがより新しいデータを上書きするのを防ぐにはどうすればよいですか?

バックフィルに独自の世代とソースウォーターマークを割り当てます。要求された範囲をカバーし、かつ新しいパーティションを置き換えない場合にのみ公開します。明示的な範囲と世代のルールによってマニフェストをマージします。

ビューがリフレッシュされる頻度よりもはるかに多くクエリされる場合はどうしますか?

最後の完全な世代を経過時間とともに提供し、鮮度の遅れによって優先順位を付け、必要に応じて人気のあるパラメータキーを事前計算します。鮮度の古さを隠したり、読み取りの需要によってリフレッシュの同時実行制限をバイパスさせたりしてはなりません。

公開情報ソース

関連する質問