Kuaishou での HiveSQL から FlinkSQL への移行の実践

要約: この記事は、Flink Forward Asia 2022 の制作実践セッションでの Kuaishou データ アーキテクチャ エンジニアの Zhang Mang と Alibaba Cloud エンジニアの Liu Dalong の共有から編集されたものです。この記事の内容は主に次の 4 つの部分に分かれています。

  1. Flink ストリームバッチ統合エンジン

  2. Flink バッチ制作の実践

  3. コア最適化の解釈

  4. これからの計画

クリックしてオリジナルのビデオとスピーチPPTを表示します

1. Flink ストリームバッチ統合エンジン

1.1 ラムダアーキテクチャ

まず最初に、ストリームとバッチの統合エンジンとして Flink を選択したときの考え方を紹介します。上の図にあるように、本番環境で最も広く使用されている Lambda アーキテクチャであり、誰もがすでによく知っていると思いますし、誰もが使用する可能性があります。Lambda アーキテクチャの利点は明らかです。

  • フレキシブル。リアルタイム リンクとオフライン リンクは完全に独立しており、実際のニーズに応じて開発され、相互に影響しません。

  • 着陸しやすい。リアルタイム リンクとオフライン リンクの両方に成熟したソリューションがあります。

もちろん欠点も明らかで、リアルタイム コンピューティングとオフライン コンピューティングの 2 つのリンクはストレージ内で再利用できないため、リソースの冗長性が深刻になります。

次に、2 つのコンピューティング エンジンの場合、一般にオフライン コンピューティングには Spark が使用され、リアルタイム コンピューティングには Flink が使用されるため、2 セットのコードを学習して維持する必要があり、コストがかかります。

一般的にリアルタイムとオフラインは2つのチームで開発・保守を行うため、実装内容や口径の統一が難しく、結果が一致しないケースが多々あります。そのため、ビジネス学生もフローバッチの一元化の実現を強く望んでいます。

1.2 エンジンの統合

ストリームとバッチの統合は、エンジンの統合とストレージの統合の 2 つの側面に分けられます。ここからが本題のエンジンの統合です。

フローバッチエンジンが統一されていれば、ユーザーは1つのエンジンを学習するだけで済み、開発したコードを大量に再利用できます。これにより、開発と保守のコストが大幅に削減され、同じ計算ロジックによりデータ品質が保証されます。また、Kuaishou オフラインジョブ切り替えエンジンは非常に便利です。したがって、私たちのエンジンの統一されたオンラインリズムとオンライン品質は制御しやすいです。

では、ストリームバッチ統合のエンジンとしてどのエンジンを使用すればよいでしょうか? 主流のビッグデータ エンジンを比較した後、ストリーミングとバッチ処理の統合エンジンとして Flink を選択しました。

Flink はストリーム コンピューティング分野のベンチマークであるため、アーキテクチャ設計ではストリームとバッチの統合が考慮されており、活発なコミュニティがあります。そして、複数のバージョンを繰り返した後、Batch はある程度利用できるようになり、以前は実稼働環境でいくつかのビジネス実装も行ってきました。

2. Flink バッチ制作の実践

次に、Flink Batch の作成とアプリケーションに焦点を当てます。現在、3,000 を超える Flink Batch ジョブをオンラインで安定して実行しています。主に、スムーズな移行のための Batch SQL です。

同時に、ユーザーにさまざまなエントリーオプションを提供します。その中でも、従来のオフライン実稼働開発に Hive 言語を使用する Batch SQL エントリも、今回の共有の焦点です。

スケジューリング プラットフォームの Flink Batch エントリは、主に Flink に精通しているユーザーが Flink 言語または API を直接使用してバッチ ジョブを開発するのに便利で、完全なオフライン スケジューリング サポートを提供します。もう一つの入り口は、事業者が自社のプラットフォームをベースにニーズに応じて構築する業務システムです。

運用環境で Hive 方言に基づいた Flink Batch を使用するにはどうすればよいですか? これらの問題に対処する必要があります。

  • オンラインのプロセスと基準を明確にする。まず、適切なジョブを選別し、オンライン化する前にデータ品質、適時性、リソースなどのさまざまな指標を検証する必要があります。
  • Hive SQL との構文互換性の問題を解決し、オーソリティ センターやメタデータ センターなどのさまざまなオフライン運用システムに接続します。
  • 本番環境の安定した動作を確保するために、オフライン環境はリアルタイム環境よりもはるかに複雑であり、リアルタイム環境には存在しないいくつかの問題が発生します。
  • 元のオフライン エンジンとのパフォーマンス ギャップを解決します。たとえば、Dalong 氏が後で紹介する動的パーティション削除ソート オペレーターの最適化などです。

これらすべての側面が解決されると、基本的にアプリケーションを昇格させることができます。

次に、Kuaishou のオフライン生産システムを簡単に紹介します。アプリケーション層には、通常、さまざまな開発プラットフォーム、またはいくつかのビジネス システムが存在します。サービス層では、Kuaishou はバッチ SQL の統合入口として HiveServer を使用し、Hive 方言を統一的に使用します。

BeaconServer は SQL の書き換え、エンジンのルーティング戦略、HBO の最適化などを行うことができます。以下のエンジン層は自由に切り替えることができるため、Flink をオフライン本番環境に接続する場合は、HiveServer を適応させて、Flink エンジンのルーティング ルールを BeaconServer に追加するだけで済みます。

現在、HiveServer へのアクセスに SQL クライアントを使用していますが、将来的には SQL ゲートウェイのサポートを拡大する可能性があります。

オフライン システムにアクセスする方法の問題を解決したら、オンラインにするプロセスを明確にする必要があります。

  • 最初のステップでは、要件を満たすバッチ SQL をフィルターで除外します。たとえば、最初は優先度の低い単純なデータ処理ジョブを選択します。
  • 2 番目のステップは、Flink を使用して SQL を解析および検証し、Flink が SQL をサポートしているかどうかを判断することです。
  • 3 番目のステップは、Flink が実行できる SQL を書き直し、挿入テーブルをテスト ライブラリ内のテーブルに変更して、操作のために送信することです。
  • 4 番目のステップは、シャドウ ジョブとオンライン ジョブの結果が一貫しているかどうか、およびリソース使用量を比較することです。
  • 5 番目のステップでは、最初の 4 つのステップで成功したジョブを Flink エンジンに切り替え、データ品質の観察を続けます。

3 番目のステップで説明したデュアル実行機能を使用すると、シャドウ ジョブには元のオフライン エンジンを使用し、オンライン ジョブには Flink を使用し、結果を比較して、予期しない問題が発生しないことを確認します。このステップは非常に重要であり、時間内に検討できなかったケースを発見するのに役立ちます。オンライン環境は非常に複雑であるため、初期段階ではより多くの観察が必要です。

現在、このプロセスは自動化されています。私たちの人材は主に、発見された異常なケースを解決することに重点を置いています。

以下に、このプロセスの重要なポイントをいくつか紹介しますので、ご参照ください。

初めて Flink を使用して SQL を検証し始めたとき、一般的に使用される構文の多くがサポートされていないことがわかり、異常だと感じました。分析の結果、Flink HiveParser のオープン方法が間違っていたために実際には使用されていないことが判明し、このブロックのコード ロジックを確認することで問題が判明しました。Flink で実際に HiveParser を使用するには、2 つの条件を満たす必要があります。

  • ハイブの方言を使用します。
  • 現在のカタログは HiveCatalog である必要があります。そうでない場合は FlinkParser にフォールバックします。

それに加えて、HiveModule が最優先であることを確認する必要があります。このようにして、Flink と Hive で同じ名前の関数が Hive に実装されます。

上図に示すように、右の方法を導入してからSQL検証の合格率が大幅に上がりました。ただし、リモート JAR の追加やディレクトリの挿入など、サポートされていないバッチ構文がまだ多くあります。

SQL の書き換えに関しては、一般に 2 つの状況があります。

  • ユーザジョブに対象テーブルに対するテーブル作成文がありません。CREATE TABLE LIKE ステートメントを使用して、最初にテスト ライブラリのターゲット テーブルを作成します。次に、生の SQL を変更してテスト ライブラリに書き込みます。
  • ユーザージョブ内のターゲットテーブルを使用したテーブル作成ステートメント。table ステートメントを直接作成し、それを変更してテスト ライブラリを作成します。次に、生の SQL を変更してテスト テーブルに書き込みます。

シャドウジョブを実行する場合は、権限の低いアカウントを使用できます。このアカウントには、SQL の書き換えが失敗した場合にオンライン ライブラリへのデータの書き込みを回避するために、テスト ライブラリを書き込む権限のみが与えられます。

品質検証に関しては、次のような戦略をとります。まず、HiveServer が記録したジョブの入力情報に基づいて、入力データ量がパーティション データと一致しているかどうかを比較します。次に、ジョブの統計情報に従って、書き込まれたデータ量が一定であるかどうかを比較します。最後に、書き込まれたデータの結果が一致しているかどうかを比較します。

比較方法は、結果データを列ごとに合計することであり、すべての列の結果が一致していれば、データの品質に問題がないことがわかります。列ごとに合計する場合、数値型の列の場合は直接合計し、非数値型の列の場合は、まずハッシュコードを取得してから合計します。

結果に一貫性がある場合は、リソースのオーバーヘッドを比較します。ここでは、YARN の統計的精度が一律に使用され、各コンテナが使用したリソース * コンテナの実行時間に応じて、最終的に総リソースが合計されます。

オンライン ジョブの標準は、データ品質に問題がなく、リソース使用量の増加が元のエンジンの 10% を超えず、実行時間が元のエンジンの 20 分を超えないことです。Flink Batch ジョブのオンライン プロセスを紹介した後、オフラインの実稼働にアクセスするにはどのような作業を行う必要があるかを見てみましょう。

上の画像に示すように、行った変更の一部がリストされています。Flink の構成は Flink、Hadoop、Hive の 3 つの部分から構成されていることが判明しましたが、その構成管理は複雑で明確ではありません。

HiveServer を介してアクセスするため、HiveServer が Flink を開始すると、Hive セッションの設定が Flink に渡されます。これには、ユーザー マニュアル セットの構成と Hadoop 関連の構成が含まれます。そこで、Flink の構成を 2 つの部分に変更し、1 つは Flink 自体の構成、もう 1 つは Hadoop と Hive の構成です。

SQL-Client では、デフォルトで単語の一部を入力し、Tab キーで単語を補完する機能が有効になっていますが、対話モードではこの機能は問題ありません。ただし、ファイルを使用して SQL を渡す場合、SQL の内容にこれが発生すると、SQL が変更され、フィールドが見つからないという例外が表示されます。そのため、ファイルからSQLを入力する場合は補完機能をオフにする必要があります。

ジョブの進捗レポートは、ユーザー エクスペリエンスにとって非常に重要な機能です。そうしないと、ジョブが送信された後、ユーザーは Hive/Spark のような進行状況情報を確認できず、HiveServer はジョブが正常に実行されているかどうかを認識できず、ジョブが常にスタックする可能性があります。そこで、進捗報告機能を実装し、長期間進捗が報告されない場合、HiveServer が積極的にジョブを強制終了します。

最後に、カンバンの監視は本当に必要です。問題を分析するときは、位置決めを支援できますが、そうでない場合は、盲目的に推測することしかできません。さらに、オフライン制作へのアクセスに関しては、プラットフォーム製品に適応するための作業がまだ残っています。

たとえば、Kuaishou のオフライン スケジューリング プラットフォームは現在、空のパーティションと SUCCESS FILE 関数を解放するための 3 つの依存関係メソッドをサポートしています。

  • タスクに依存します。上流タスクが成功すると、下流タスクがプルされます。
  • パーティションに依存します。パーティション メタデータが生成されたかどうかを検出し、生成後にダウンストリーム タスクを開始します。
  • 成功ファイルは依存します。ファイルが存在するかどうかに応じて、下流タスクをプルアップするかどうかを決定します。

Flink はシンクに従ってファイル ディレクトリを書き込み、どのパーティションを解放する必要があるかを決定します。動的パーティションの場合は問題ありません。静的パーティション書き込みタスクであり、同時にデータが生成されない場合、Flink はパーティションを公開しないため、ダウンストリームがプルアップされない可能性があります。また、SUCCESS FILEが書き込まれていない場合も同様の問題が発生します。

統計情報の収集に関しては、Flink Batch は元々統計情報を収集しませんでしたが、パーティションが生成された後、メタデータ センターではデータが 0 と表示されます。これを見たユーザーは、ジョブが正常に実行されなかったと考え、ジョブを再実行します。ユーザーがデータ品質検証を構成し、統計情報がない場合も検証は失敗します。

アクセス関連のコンテンツを紹介した後、オンラインで実行した後に発生する問題を見てみましょう。

オフライン生産は通常 T+1 であることがわかっています。前日のデータの処理が0時以降に開始されるため、0時以降に大量のジョブがスケジュールされ、オフラインのリソースが非常に逼迫します。この時点で開始されたベースライン ジョブはリソースを取得できない可能性があります。ベースライン ジョブが時間どおりに完了することを保証するために、YARN は低品質のジョブを含む一部のコンテナーを強制終了し、リソースをベースライン ジョブに割り当てます。

Flink は通常、タスク失敗の合計数がしきい値に達すると、一定期間内にジョブを失敗します。オフライン エンジンは通常、ジョブが失敗する前に同じタスクに数回失敗します。オフライン エンジンはプラットフォームによって引き起こされた失敗をカウントしません。

Flink Batch を起動すると、リソースのプリエンプションの問題が発生しました。ジョブは一定期間実行すると失敗し、スケジューリング プラットフォームの失敗による再試行がトリガーされ、数回の再試行後に成功します。一部のジョブは失敗しない場合がありますが、タスクが削除され、データを再計算する必要があるため、実行時間が長くなります。

この問題を解決するには、タスク失敗のしきい値を単純に増やすだけでは解決できません。ビジネスロジックに起因するタスク障害が発生し、障害しきい値を大きくすると、例外の検出が間に合わず、重大な事故が発生します。

したがって、タスクが失敗したときに特定の失敗理由を取得するために、オフライン エンジンの実践を参照します。リソースのプリエンプションやマシンのオフラインなどのプラットフォーム上の理由が原因の場合、失敗の数はカウントされません。これにより、Flink ジョブの再試行が頻繁に失敗するという問題が解決されます。ユーザーが実行時間が長すぎると感じた場合は、ジョブの優先順位を調整することを検討する必要があります。

リソースのプリエンプションの問題を解決した後、オフライン クラスターの低速ノードの問題は、安定性に対するもう 1 つの隠れた危険です。オフライン クラスターでは過剰な CPU 使用率とビジー IO が非常に一般的であり、個々のタスクのロングテールによりジョブ全体の実行時間が長くなります。

この問題の解決策は非常に簡単で、オフライン コンピューティングの投機的実行を参照するだけです。タスクの実行時間が一定期間の同じタスクの平均実行時間を超えていることが判明した後。スケジューラは他のノード上のミラーリングされたタスクをプルアップし、最初に実行されたタスクがそのタスクのデータを使用します。

この機能は Kuaishou とコミュニティによって共同開発されたもので、Flink のデータ断片化は動的に割り当てられるため、Hive や Spark の静的なメカニズムとは異なります。したがって、Source の投機的実行の実装の複雑さは非常に高くなり、リソースのプリエンプションなどの異常なケースも考慮する必要があります。

集約ジョブの開始により、一部の単純な集約コンピューティング タスクの実行時間が非常に不安定で、非常に速い場合もあれば、異常に遅い場合もあることがわかりました。注意深く分析した結果、Flink はデフォルトで Shuffle を実行するために TaskManager を使用することがわかりました。Shuffle データがダウンストリームで完全に消費されない場合、TaskManager は解放できません。これにより、次の 2 つの問題が生じます。

  • 資源の無駄遣い。アイドル状態のタスクマネージャーは解放できません。
  • この時点でリソースのプリエンプションが発生するか、マシンがオフラインになってタスク マネージャーが強制終了されると、シャッフル データが失われ、データのこの部分を再計算する必要があるため、ジョブの実行時間が長くなります。

この問題を解決するには、Shuffle Service を TaskManager から分離する必要があります。実装アイデアは 2 つあります。

  • Hive や Spark と同様に、Yarn NodeManager に基づいた Shuffle Service を使用します。ただし、Flink はまだ実装していないため、自分たちで開発する必要があります。
  • リモートシャッフルサービスをご利用ください。Flink にはオープンソース実装があり、Kuaishou にも自社開発の Remote Shuffle Service があります。

調査の結果、Kuaishou が開発したリモート シャッフル サービスを選択しました。Kuaishou のリモート シャッフル サービスはプッシュベースのシャッフルをサポートしているためです。Shuffle Service は同じ Shuffle パーティションのデータをマージし、タスクはすべての Shuffle データを 1 か所から読み取ることができます。将来的にはコミュニティの Remote Shuffle Service もこの機能をサポートする予定です。

次に、Kuaishou のリモート シャッフル サービスにはエンドツーエンドのデータ整合性検証があり、データ品質が十分に保証されています。

移行ワークロードの増加に伴い、デフォルトの同時実行設定がほとんどのジョブにとって最適ではないという非常に難しい問題に直面しています。

リアルタイム コンピューティングのシナリオでは、ジョブの同時実行性はユーザーによって設定されます。ただし、オフライン コンピューティングの場合、ユーザーは同時実行度を設定する必要はなく、エンジンがデータ量に応じて対応する同時実行度を自動的に計算します。同時実行性を手動で設定するのは現実的ではありません。データ量は毎日変化するため、毎日同じ同時実行性を使用できるわけではありません。

同時実行性を手動で設定する必要がある場合、Hive/Spark ジョブをスムーズに移行するという目標を達成することはできません。私たちはコミュニティと協力してこの問題を解決し、Adaptive Scheduler がデータ量に基づいて適切な同時実行性を自動的に推定するため、スムーズな移行を実現するためにユーザーのジョブを変更する必要はありません。

また、小さなファイルのマージの同時実行性は、現時点では Adaptive Scheduler では正確に推定できませんが、Hack によって一時的に解決され、コミュニティは将来的にこの特殊なケースに対する API サポートを拡張する予定です。

現在、集約クラスのバッチ ジョブを徐々に増やしており、2 つの複雑な問題に遭遇しましたが、コミュニティと協力して解決しています。

  • ハイブ UDAF のサポート。現在、Flink は Partial1 モードと Final モードの Hive UDAF のみをサポートしており、当面は Rank などの機能をサポートできません。
  • ハッシュ集約のサポート。現在、Hive UDAF を使用するジョブは Sort Agg を使用しており、Hash Agg と比較したパフォーマンスの違いは依然として明らかです。

集約ジョブのスムーズな移行を促進するには、Hash Agg と完全な Hive UDAF のサポートが非常に必要です。

3. コア最適化の解釈

Flink Batch は、Kuaishou を起動する過程で、構文の互換性、Hive コネクタ、安定性、その他の側面を含む多くの問題に遭遇しました。これらの問題に対して、Kuaishou とコミュニティは協力して問題を解決し、Flink Batch の立ち上げを成功裏に推進しました。次に、可用性、使いやすさ、安定性の観点から、コミュニティによって行われた最適化と改善の取り組みを紹介します。

Flink は標準の ANSI SQL であるため、Hive SQL には ANSI SQL と多くの構文の違いがあります。Hive SQL を Flink SQL エンジンに移行するために、Kuaishou は Hive Dialect を使用することを選択しました。この場合、ほとんどのジョブは、ユーザーが SQL を変更する必要なく移行できます。Flink 1.16 より前ではありますが、コミュニティは Hive Dialect の互換性に関して多くの作業を行ってきました。しかし、Hive SQL との完全な互換性を実現するにはまだギャップがあります。Kuaishou が移行するジョブのバッチを選択した後、分析と検証を通じて、サポートされていない文法が多数発見されました。

Kuaishou が意見を述べた後、コミュニティはサポートを優先しました。上の図に示すように、CTAS、ADD JAR、USING JAR、マクロ コマンド、Transform など、重要で一般的に使用される文法をいくつか示します。

UDF は Hive SQL でよく使用されます。通常、ユーザーはまずリモート UDF JAR をジョブに追加し、次にそれを登録して使用します。Flink では現在、JAR の追加がサポートされていないため、多くのジョブを移行できません。さらに、アルゴリズムの学生は Java UDF を書きたがらず、通常は Python でスクリプトを作成し、変換を通じてデータを処理します。Hive Dialect 構文を完成させると、移行プロセスの最初のブロックが解決されます。既存の Hive SQL が Flink エンジン上で実行できることが保証されました。

コミュニティは、Hive 構文を完成させるために Flink 1.16 で多くの作業を行ってきました。現在、qtest テストを通じて、全体的な互換性は 95% に達し、ユーザーの既存のクエリを Flink に移行できることを基本的に保証できます。Flink-25592 と Flink-26360、これら 2 つの包括的な問題は、Flink Batch に関連する作業を追跡しています。CTAS&USING JAR の 2 つの機能は PUBLIC API の変更を伴うため、コミュニティに対応する FLIP 設計ドキュメントが存在します。次に、この部分の設計について詳しく紹介します。

上図に示すように、まず JAR 関数を使用した FLIP-214 Create Function を導入します。この関数には SQL モジュールの ClassLoader の変更が含まれるためです。したがって、ClassLoader の落とし穴を踏まないように、設計上のアイデアを全員に紹介する必要があります。

SQL を作成する人なら誰でも、ビジネス ロジックが多様であるため、コンピューティング エンジンの組み込み関数ではニーズを満たせないことがよくあることを知っています。この場合、ユーザーは要件、特に Java テクノロジー スタックのビッグ データ エンジンを満たすように UDF を作成する必要があります。UDF を JAR パッケージに入れて、リモート HDFS アドレスにアップロードします。使用する場合は、最初に JAR を追加するか、JAR パッケージに基づいて直接 UDF を作成します。

このシナリオと Kuaishou のビジネス ニーズを考慮して、コミュニティは 1.16 で USING JAR 関数をサポートしました。PPT で赤でマークされたフォントなど、文法部分全体には、以前よりも多くの USING JAR キーワードが含まれており、リモートまたはローカルの JAR パッケージのアドレスを指定できるようになりました。現在、この構文は Java および Scala 言語に対してのみサポートされています。

次に、USING JAR関数の使い方と実行の仕組みを詳しく紹介します。まず、UDF を登録します。UDF を登録するプロセスでは、UDF の DDL を解析し、最初に関数が一時的なものであるかどうかを判断します。そうでない場合は、特別な作業を行わずにカタログに直接登録されます。一時的な場合は、JAR パッケージのアドレスがローカル ファイルであるか、リモートの HDFS または OSS アドレスであるかを判断します。

ローカル JAR の場合は、JAR パッケージが正当であるかどうかがチェックされ、JAR パッケージが正当である場合は、JAR パッケージのアドレスが ResourceManager と MutableURLClassLoader に追加されます。

Flink Table モジュールで頻繁に発生する Connector&Catalog に関連する ClassLoader の問題を解決するには、ここで追加の説明が必要です。バージョン 1.16 では、コミュニティは Table モジュールに MutableURLClassLoader を導入しました。各 TableEnvironment は ClassLoader を保持し、JAR パッケージを ClassLoader に動的に追加できるようになり、JAR パッケージの動的ロードの問題が解決されます。

次に、JAR パッケージを FunctionCatalog に登録して管理します。JAR パッケージがリモート アドレスにある場合、追加のダウンロード アクションが発生します。このアクションは、JAR パッケージをローカルの一時ディレクトリにダウンロードし、同時に MutableURLClassLoader にロードする ResourceManager によって完了します。

2 番目のステップは UDF を使用することです。UDF がジョブのクエリで使用される場合、クエリの解析と最適化のプロセスで、最初に関数が一時的であるか永続的であるかを判断します。後者の場合、カタログから JAR アドレス情報を取り出し、まず JAR パッケージをローカルにダウンロードして ClassLoader にロードし、次にクエリを最適化して JobGraph を生成します。

JobGraph が生成された後の 3 番目のステップは、ジョブをクラスターにデプロイして実行することです。Query を最適化するときに JAR パッケージが必要で、クラスター上で実行するときにもこれらの JAR パッケージが必要です。そうしないと、ジョブの実行中に ClassNotFoundException が発生します。では、どうすればよいでしょうか?

ここでは Flink の BlobServer を使用します。クラスターにジョブを送信するときは、最初に ResourceManager で維持されているすべてのローカル JAR パッケージを Flink JobManager の BlobServer (図の黄色の点線でマークされた部分) にアップロードします。ジョブが実行されると、TM の責任でこれらの JAR パッケージを BlobServer からプルします。

次に、もう 1 つの一般的に使用される関数 CTAS を紹介します。この構文はすべてのビッグ データ コンピューティング エンジンでサポートされており、CREATE TABLE 構文と比較すると、テキスト内の赤でマークされたフォントが異なります。

この構文の機能は、エンジンが SELECT クエリに基づいてターゲット テーブルのスキーマを自動的に推論し、カタログがそのスキーマを作成することです。これは、最初にターゲット テーブルを作成してから、挿入を書き込むのと同じです。 .select query. 最大の利点は、クエリが複雑な場合に、ユーザーがターゲット テーブルの DDL を手書きする必要がなくなり、ユーザーの作業負荷が簡素化されることです。この機能は運用環境で非常に役立ちます。

次に、CTAS の全体的な実装プロセスを紹介します。まず、ユーザーが CTAS クエリを作成し、クライアントのコンパイルと最適化のプロセス中に、まずクエリに基づいてターゲット テーブルのスキーマを導出します。次に、対応するカタログをシリアル化します。シリアル化の目的は、ジョブマネージャー上でテーブルを逆シリアル化するためにテーブルを作成するアクションを実行することです。同時に、カタログを呼び出してジョブマネージャーにターゲットテーブルを作成するフックオブジェクトを生成します。

2 番目のステップはジョブの実行です。ジョブのスケジュールを開始する前に、まず JobManager 上のフック オブジェクトとカタログ オブジェクトを逆シリアル化します。次に、フックによってカタログが呼び出され、最初にターゲット テーブルが作成されます。次に、ジョブをスケジュールします。

最終的にジョブが正常に実行されると仮定すると、追加のアクションはありません。ジョブの実行が失敗するか手動でキャンセルされた場合、原則的な考慮事項から、フックを介してカタログを呼び出し、作成されたターゲット テーブルを削除し、最終的に外部システムに副作用がないことを確認します。

Flink がストリームとバッチの統合コンピューティング エンジンであることを考慮すると、CTAS 構文はストリームとバッチの両方のシナリオで使用できます。ただし、一般にストリーミング シナリオでは、ジョブが失敗した場合、テーブルを手動で削除せず、外部システムの更新機能に依存してデータの最終的な整合性を確保します。

したがって、データの原子性を保証するかどうかをユーザーが決定できるように、原子性関連のオプションを導入します。コミュニティは Flink 1.16 で CTAS の基本機能を完成させただけであり、アトミック性はまだサポートしていません。これは 1.17 で完成する予定です。詳細については、FLIP-218 の設計ドキュメントを参照してください。

Kuaishou Flink バッチの実践中に、Hive コネクタの多くの側面で問題が見つかりました。たとえば、分割コンピューティングの高速化、統計情報の収集、小さなファイルのマージなどです。上図に示すように、使用時に比較的重要な機能がいくつかリストされています。

これらの最適化により、Hive コネクタの機能が強化され、バッチ シナリオでより使いやすくなりました。次に、動的パーティション書き込みの最適化と小さなファイルのマージについて詳しく紹介します。

静的パーティションへの書き込みとは異なり、ユーザーは常にパーティション列の値を指定する必要があります。動的パーティショニングにより、ユーザーはデータの書き込み時にパーティション列の値を指定しなくても済みます。

たとえば、このようなパーティション テーブルがあります。ユーザーは次の SQL ステートメントを使用してデータをパーティション テーブルに書き込むことができます。

この SQL ステートメントでは、ユーザーはパーティション列の値を指定していません。これは、動的パーティション書き込みの典型的な例です。

Flink では、対応する生成されたプランは何ですか? 右側の実行計画図に示すように、ここには 4 つのノードがあります。その中で、灰色の Sort ノードに注目する価値があります。Flink が動的パーティションに書き込む場合、まず動的パーティション列に従ってデータを並べ替えてから、データを 1 つずつパーティションに書き込みます。

これにはいくつかの利点がありますが、ジョブの実行時間が長くなる原因にもなります。したがって、この状況と Kuaishou のビジネス シナリオに対応して、ユーザーが動的パーティションに書き込むときに Sort ノードを手動で閉じて、追加の並べ替えを回避し、ダウンストリーム データの出力を高速化できるオプションを導入しました。

小さなファイルの問題も、運用環境では非常に一般的な問題です。Hive テーブルに書き込む場合、書き込み速度を確保するために、ジョブの同時実行設定は比較的大きくなります。書き込み速度が向上する一方で、小さなファイルの問題も発生します。

ファイルが小さいと、HDFS NameNode の圧力と RPC の圧力が増加するため、ダウンストリームの読み取りタスクには好ましくありません。さらに、動的パーティションが書き込まれるとき、特定の同時実行性によって多くの動的パーティションが同時に書き込まれ、多数の小さなファイルが生成される可能性があります。上記の問題を踏まえ、Hive Batch で記述し、小さなファイルの適応的マージをサポートします。

上の図は、バッチ モードでの小さなファイルのマージをサポートする Hive シンクのトポロジを示しています。グラフには、Writer、CompactorCoordinator、Rewriter、PartitionCommitter という 4 つのノードがあることがわかります。ここでの中心となるのは、CompactorCoordinator と Rewriter です。

CompactorCoordinator は単一の同時ノードであり、上流の Writer がファイルの書き込みを完了すると、CompactorCoordinator にファイル パス情報を伝えます。CompactorCoordinator はすべての上流ファイルを取得した後、どのファイルが小さいファイルであるか、およびマージする必要があるターゲット ファイルのサイズを判断し、どの小さいファイルを大きいターゲット ファイルにマージするかを決定します。

次に、その情報を Rewriter に伝えると、Rewriter がマージ作業を完了し、最後に PartitionCommitter がパーティション情報を送信します。小さなファイルを適応的にマージする利点は、ファイル数が減って HDFS への負荷が軽減されること、ユーザー ジョブのデータ読み取り効率が向上すること、実行速度が向上することです。

次に、UDAF を使用する過程で遭遇するパフォーマンスの問題について説明します。まず、Sort-Agg と Hash-Agg の 2 つの概念を紹介します。一般に、集約コンピューティングのシナリオには、Sort-Agg と Hash-Agg という 2 つの戦略があります。

Sort-Agg は、集計計算の前に、キーごとにグループに従ってデータをグローバルに並べ替えます。ソート後、すべてのデータを走査し、同じキーを持つデータが見つかった場合は累積を行います。異なるキーを持つデータが見つかった場合、前のグループのすべてのデータが計算されたことを意味し、結果を下流に直接送信できます。次に、新しいキーに対応するグループの集計値を計算します。

Hash-Agg はメモリ上にハッシュ テーブルを構築することを指します。キーは group by のキー、値は各グループを上向きに累積した集計値です。すべてのデータの走査が完了すると、最終結果を出力できます。一般に、Hash-Agg はメモリ内で完了するため効率的ですが、Sort-Agg は 1 ステップの外部ソートが必要なため、パフォーマンスは比較的低くなります。

現在、Flink には、ImperativeAggregateFunction と DeclarativeAggregateFunction という 2 つの集約コンピューティング関数インターフェイスがあります。2 つのインターフェイスの対応する UDAF 実装の長所と短所は、上の図の左側にリストされています。

現在、Hive UDAF は Sort-Agg 戦略のみを採用でき、全体的なパフォーマンスは比較的劣っています。この問題に対応して、調査の結果、DeclarativeAggregateFunction インターフェイスに基づいて、Hive の一般的に使用されるいくつかの UDAF を Flink で再実装することにしました。ここで難しいのは、Hive の動作と一貫性を持たせることです。再実装後は、ほとんどのクエリで Hash-Agg が使用できるようになり、全体的に組み込み関数と同等のパフォーマンスが得られます。

次に、もう 1 つの重要な機能である適応スケジューラについて説明します。Flink ストリーミング ジョブを作成したユーザーは、Flink ジョブをオンラインにする前に同時実行性を設定する必要があることを知っています。ストリーミング ジョブの場合、これは誰もがデフォルトで受け入れるものです。しかし、バッチ ジョブの場合、状況はさらに複雑になります。

まず、多くのバッチ ジョブ (多くの場合、数百、数千、場合によっては数万) があり、ユーザーがケースごとに調整して同時実行することは不可能であり、時間と労力がかかります。

次に、データ量は毎日変化する可能性があり、予測が困難です。したがって、同じ同時実行設定が同じジョブに常に適用できるとは限りません。ジョブの実行時間が常に安定した時間ベースラインの範囲内にあるという保証はなく、実稼働に比較的大きな影響を及ぼします。

最後に、ソースとシンクを除く SQL ジョブは、グローバルに統合された並列処理でのみ構成でき、きめ細かい並列処理を設定することはできず、リソースの無駄と追加のオーバーヘッドも発生します。

これらの問題を解決するために、コミュニティは Flink 用の適応型バッチ スケジューラを導入しました。それを通じて、フレームワークは、コンピューティング ノードが処理する必要があるデータの量に応じて、ノードの並列処理を自動的に導き出します。

この種の並列処理構成は比較的一般的であり、各ジョブを個別に構成する必要がなく、ほとんどのジョブに適用できます。自動的に設定される並列度は、毎日異なるデータ量に適応できます。同時に、各ノードが実際に処理する必要のあるデータ量を実行時に収集できるため、きめ細かい並列度を設定できます。そのプロセスは大まかに次のとおりです。

  • 上流論理ノードのすべての実行ノードが終了すると、それらによって生成されたデータ量を収集します。
  • ダウンストリーム論理ノードによって消費されるデータの量が決定されると、並列度を通じてポリシー コンポーネントを導出し、ノードの適切な並列度を計算できます。
  • 論理ノードの並列性が決定したら、その実行ノードを実行トポロジに追加し、スケジュールしてデプロイしてみます。

従来の Flink ジョブ実行との違いは、以前のジョブ実行トポロジはジョブが送信されたときに構築され、静的であったことです。適応型バッチ スケジューリング ジョブの場合、実行トポロジは動的に生成されます。動的実行トポロジでは、下流ノードは複数のサブパーティションを消費できるため、上流ノードの実行プロセスは下流ノードの並列処理から切り離されます。

適応型バッチ スケジューリングと Hive Source の同時実行導出機能を組み合わせることで、同時実行設定の問題が解決されます。クアイショウ側で得られる効果は、主に次の 2 つの側面に反映されます。

  • この機能を使用すると、ユーザーはジョブごとに並列処理を個別に構成する必要がなくなり、Flink Batch が使いやすくなり、きめ細かい並列処理設定がサポートされ、リソースの無駄が回避されます。
  • データ量に応じて、オペレーターの同時実行性が自動的に調整されるため、本番環境でのジョブの安定した運用、出力ベースラインの確保、ジョブの移行とオンライン化がスムーズに行われます。

次に、コミュニティとKuaishouが生産安定性の観点から協力して取り組んだ比較的重要な機能である投機的実行を紹介します。運用環境では、一般的にホットスポット マシンは避けられず、混合クラスターや集中的な更新データにより、マシンの負荷が高くなり、IO がビジーになり、マシン上で実行される Flink ジョブが非常に遅くなる可能性があります。時折起こる機械の異常によっても、同じ問題が発生する可能性があります。

これらの遅いタスクはジョブ全体の実行時間に影響を与えるため、ジョブの出力ベースラインは保証できません。投機的実行は、このような問題を解決するための方法として広く認識されています。したがって、コミュニティはこのメカニズムを Flink バージョン 1.16 に導入しました。

投機的実行が有効になった後、バッチ ジョブ内のサブタスクが他のサブタスクよりも大幅に遅いことがフレームワークによって検出されると、そのサブタスクに対して新しい実行インスタンスが作成されます。これをシャドウ タスクと呼び、通常のマシン ノードにデプロイされますが、元の低速タスク インスタンスは保持されて実行され続けます。

これらのシャドウ タスクには、対応する元のタスクと同じ入力と出力があります。このうち、最初に完了したタスクが認識され、出力データは下流ノードでの消費に使用でき、他の対応するインスタンスはキャンセルされ、出力データはクリアされます。

投機実行の具体的なプロセスは図の通りで、SlowTaskDetectorは遅いタスクがあることを発見すると、投機実行スケジューラに通知します。スケジューラは、遅いタスクが存在するマシンをホットスポット マシンとして識別し、ブラックリストに追加します。その後、遅いタスクの実行中の実行インスタンスの数が上限に達していない場合、スケジューラはそのタスク用に新しい実行インスタンスを作成してデプロイします。いずれかの実行インスタンスが正常に終了すると、スケジューラは、そのインスタンスに対応する実行ノードの他のすべての実行インスタンスをキャンセルします。

先ほど、フレームワーク レベルで実装される一般的な投機実行プロセスを紹介しましたが、ソースとシンクの場合、投機実行にはいくつかの特別な機能があります。Source ノードの場合、同じ Source の異なる同時実行インスタンスが常に同じデータを読み取るようにする必要があります。この方法でのみ、結果の正確性が保証されます。ここで考慮すべき特殊なケースがいくつかあります。

  • FLIP-27 の新しいソースでは、ソース側のスプリットが動的に割り当てられるため、シャドウ タスクと元の低速タスクが同じスプリットを処理するようにする必要があります。
  • 元の遅いタスクがすでに処理した分割の一部は、シャドウ タスクの処理が速くなり、以前の分割に追いつくことができます。このとき、シャドウ タスクはより多くのスプリットを割り当てるように要求します。このプロセスでは、元の低速タスクが同じスプリットを処理することも必要です。これは相互競馬プロセスである可能性があり、最初に実行を終了した人がそのデータを使用することになります。
  • リソースの横取りやマシンの異常などにより、シャドウタスクや遅いタスクがハングアップする場合があります。1 つのタスクのみが一時停止されている場合は無視できますが、両方のタスクが一時停止されている場合は、投機実行スケジューラがそれを決定して認識した後、処理された分割情報を返し、これらの分割を処理する新しいタスクをスケジュールする必要があります。

一般的に、キャッシュはフレームワーク層に追加され、各ソースによって同時に取得されたデータフラグメントと、その下のすべての実行インスタンスによって処理されたフラグメント情報を記録します。

シンク側の場合は、データの書き込みのみを担当するため、状況は非常に単純で、シャドウ タスクと低速タスクが最初に実行されることを確認し、次にそのデータを送信し、そのデータをクリーンアップするだけで済みます。データの重複を避けるために別の無効なシンクを追加します。

投機的実行機能により、バッチ タスクの実行の安定性が保証され、出力時間は比較的安定して制御可能です。これにより、Kuaishou の運用および使用における Flink バッチの全体的な安定性が確保され、さらなるバッチ実装のための良好な基盤が築かれます。

上記は、コミュニティと Kuaishou の協力によって Batch で行われたいくつかの中核的な最適化と改善作業を、使いやすさ、使いやすさ、安定した生産性の 3 つの側面から紹介するものです。これらのタスクにより、Flink Batch が Kuaishou で起動され、運用環境で安定して実行されるようになります。Kuaishou in Batch のさらなる進歩に伴い、将来的には多くの作業面で取り組む必要があるでしょう。

4. 今後の計画

上図の通り、Flink Batchの方向に向けて投資を継続していきます。監視指標の表示と履歴サーバーの可用性は、問題の特定と分析を容易にするためにできるだけ早く完了する必要があり、いくつかの簡単な問題はユーザーが自分で解決できます。さらに、集約シナリオの関連問題が解決されると、多数の集約ジョブを移行できます。結合シナリオの問題を解決した後、複雑な結合シナリオ ジョブの移行が開始されます。

ストリームとバッチの統合ストレージの検討では、エンジン容量が構築された後、統合ストレージ サービスが構築され、ストリーム ジョブとバッチ ジョブに統合された読み取り/書き込み API を提供し、冗長ストレージによって引き起こされるコストの問題を解決します。

クリックしてオリジナルのビデオとスピーチPPTを表示します


 
さらに多くのコンテンツ
 
イベントの推奨事項 Apache Flink に基づく Aliyun のエンタープライズ レベルの製品 - リアルタイム コンピューティングの Flink バージョンがオープンしました。
0 元のトライアル リアルタイム コンピューティング Flink バージョン (5000CU* 時間、3 か月以内)
 
プロトコル バッファよりも無限に高速です. オープン ソースの 10 年を経て、Cap'n Proto 1.0 がついにリリースされました. 華中科技大学の博士研究員が LK-99 磁気浮上現象を再現しました. Loongson Zhongke は新世代の開発に成功しましたプロセッサの Loongson 3A6000 ミニブリンク バージョン 108。 世界最小の Chromium コア ChromeOS は、ブラウザとオペレーティング システムを独立した 1TB ソリッド ステート ドライブに分割し、テスラ チャイナ モールで価格 2,720 元 ファーウェイは HarmonyOS 4 のセキュリティ アップグレード バージョンを 正式にリリースし、 すべての Electron ベースのアプリケーションがフリーズ AWS は来年 IPv4 パブリック ネットワーク アドレスのサポートを開始 命令 型プログラミング言語 Nim v2.0の正式リリース
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/2828172/blog/10088190