Spark のデータ スキューのシナリオとソリューション

記事ディレクトリ

データスキュー発生時の現象

ほとんどのタスクは非常に高速に実行されますが、個々のタスクの実行は非常に遅くなります。

データスキューの原理

シャッフルを実行する場合、各ノードの同じキーを特定のノードのタスクにプルして、キーに従って集計や結合などの操作を実行する必要があります。このとき、あるキーに対応するデータ量が特に多い場合、データの偏りが発生します。したがって、データの偏りがあると、Spark ジョブの実行が非常に遅くなり、タスクによって大量のデータが処理されるためにメモリ オーバーフローが発生することさえあります。ステージ全体の実行速度も、最も遅いタスクによって決まります。10/100万

データ スキューの原因となっているコードを特定する方法

データのスキューは、シャッフル中にのみ発生します。シャッフル操作をトリガーする可能性のある演算子:distinct、groupByKey、reduceByKey、aggregateByKey、join、cogroup、repartition など。

タスクの実行が非常に遅い場合

データスキューが発生する段階です。yarn-client モードで送信すると、ログをローカルで直接見ることができ、ログで現在実行中のステージを見つけることができ、yarn-cluster モードで送信すると、Spark Web UI ステージを通じて現在実行中のステージを確認できます。
Spark Web UI で、現在のステージの各タスクによって割り当てられたデータの量を詳しく調べて、タスクの不均一なデータ割り当てがデータ スキューにつながるかどうかをさらに判断します。
データのスキューが発生しているステージがわかったら、ステージ分割の原理に基づいて、スキューが発生しているステージがコードのどの部分に対応するかを計算する必要があります.コードのこの部分にシャッフル演算子が存在する必要があります. 通常、シャッフル オペレーターはステージを分割します。

タスクが不可解にメモリをオーバーフローする

yarn-client モードのローカル ログで例外スタックを表示するか、YARN を介して yarn-cluster モードのログで例外スタックを表示します。一般的に言えば、例外スタック情報を使用して、コードのどの行でメモリ オーバーフローが発生しているかを特定できます。次に、そのコード行を見回すと、通​​常はシャッフル演算子があり、この時点で、この演算子はデータ スキューを引き起こす可能性があります。ただし、メモリ オーバーフローは必ずしもデータ スキューが発生したことを意味するわけではなく、コードにバグがある可能性もあります。
データの偏りの原因となるキーのデータ分布を確認する
データの偏りがどこで発生しているかがわかった後、通常、シャッフル操作を実行してデータの偏りを引き起こしたRDD/Hiveテーブルを分析し、その中のキーの分布を確認する必要があります。 . これは主に、後でどの技術ソリューションを選択するかの基礎を提供するためのものです。

  • Spark SQL の group by ステートメントと join ステートメントが原因でデータ スキューが発生している場合は、SQL で使用されるテーブルのキー分散をクエリします。
  • Spark RDD でシャッフル オペレーターを実行することによってデータ スキューが発生する場合は、コードを追加して、RDD.countByKey() などの Spark ジョブでキーの配布を確認できます。カウントされた回数は、クライアントに収集/持参して印刷すると、キーの配布が表示されます。

データ スキュー ソリューション

1. Hive テーブル自体のデータは非常に不均一です - Hive ETL を使用してデータを前処理します

Hive ETL は事前にキーに従ってデータを集計するか、事前に他のテーブルと結合し、Spark ジョブで対象となるデータ ソースは元の Hive テーブルではなく、前処理された Hive テーブルです。このとき、データは事前​​に集計または結合されているため、元のシャッフル オペレーターを使用して、Spark ジョブでそのような操作を実行する必要はありません。
このソリューションは、Spark でのシャッフル オペレーターの実行を完全に回避するため、ルートからのデータ スキューの問題を解決します。したがって、データ スキューの問題は絶対にありません。しかし、インデックスは根本的な原因を解決するものではなく、Hive ETL でもデータ スキューが発生します。

2. スキューの原因となるキーはわずかしかなく、計算自体への影響はほとんどありません - スキューの原因となるいくつかのキーをフィルタリングします

大量のデータを持ついくつかのキーが、ジョブの実行と計算結果にとって特に重要でない場合は、単純にそれらのいくつかのキーを除外します。データ スキューの原因となるキーを除外すると、これらのキーは計算に参加しなくなり、データ スキューを生成することは当然不可能になります。

3. データの傾きに対応する必要がある - シャッフル操作の並列性を改善する

For shuffle statement in Spark SQL, such as group by, join, etc., you need to set a parameter, すなわち spark.sql.shuffle.partitions, which represents the parallelism of the shuffle read task. デフォルト値は 200 です.多くのシナリオ 少し小さすぎます。シャッフル読み取りタスクの数を増やすことで、本来 1 つのタスクに割り当てられていた複数のキーを複数のタスクに割り当てることができるため、各タスクが以前よりも少ないデータを処理できるようになります。
短所: このソリューションでは、通常、データ スキューを完全に解決することはできません。100 万データに対応するキーなどの極端なケースがある場合、タスク数がいくら増えても、100 万データに対応するキーは依然として存在する必要があるためです。処理のためにタスクに割り当てられるため、データ スキューが引き続き発生する運命にあります。

4. 集計クラス(reduceByKey /group by)シャッフル二段集計(ローカル集計+グローバル集計)

このソリューションのコア実装のアイデアは、2 段階の集計を実行することです。1回目は部分集計です.まず各キーに10以内の乱数などの乱数をつけます.このとき,元の同じキーが (hello, 1) (hello, 1) ( hello, 1) (hello, 1) は (1_hello, 1) (1_hello, 1) (2_hello, 1) (2_hello, 1) になります。次に、乱数でマークされたデータに対して reduceByKey などの集計操作を実行してローカル集計を実行すると、ローカル集計結果は (1_hello, 2) (2_hello, 2) になります。次に、各キーのプレフィックスを削除すると、(hello, 2)(hello, 2) になり、グローバル集計操作を再度実行して、(hello, 4) などの最終結果を取得します。
元の同じキーにランダムなプレフィックスを追加し、それを複数の異なるキーに変換することで、1 つのタスクで処理されたデータを複数のタスクに分散してローカルに集約できるため、1 つのタスクの質問で処理されるデータが多すぎるという問題が解決されます。次に、ランダムなプレフィックスを削除し、グローバル集計を再度実行して最終結果を取得します。

5. 小さなテーブルを大きなテーブルに結合 - リデュース ジョインをマップ ジョインに変換

より小さい RDD 内のデータを収集演算子を介してドライバー側のメモリに直接プルし、そのためのブロードキャスト変数を作成してから、別の RDD でマップ演算子を実行し、演算子関数でブロードキャスト変数から取得します。小さい方のRDDの全量のデータを取得し、接続キーに従って現在のRDDの各データと比較し、接続キーが同じ場合は、2つのRDDのデータを必要な方法で接続します。通常の結合はシャッフル処理を経て、一度シャッフルすると、同じキーのデータをシャッフル読み込みタスクに引っ張ってから結合を実行するのと同じです。ただし、RDD が比較的小さい場合は、ブロードキャストの小さい RDD フル データ + マップ演算子を使用して、結合と同じ効果、つまりマップ結合を実現できます。このとき、シャッフル操作はなく、データ スキューもありません。発生します。

6. 2 つのテーブルのデータ量は比較的多いが、そのうちの 1 つのいくつかのキーのデータ量が大きすぎ、もう 1 つのテーブルは偏ったキーを均一にサンプリングし、結合操作を分割できます

  • 大量のデータを持ついくつかのキーを含む RDD の場合、sample 演算子を使用して、どのキーが最大量のデータを持つかをサンプリングして計算します。
  • 次に、これらのキーに対応するデータを元の RDD から分割して個別の RDD を形成し、各キーにプレフィックスとして n 以内の乱数を与えます。ほとんどの歪んだキーが別の RDD を形成することはありません。
  • 次に、結合する必要がある別のRDDも、それらの傾斜したキーに対応するデータを除外して別のRDDを形成し、各データをn個のデータに展開し、これらのn個のデータに0〜nが追加されますスキューを引き起こさないキーのほとんどは、別の RDD も形成します。
  • 次に、ランダムなプレフィックスを追加した独立 RDD を、n 倍に拡張された別の独立 RDD と結合します. このとき、元の同じキーを n 個の共有に分割し、複数のタスクに分散して結合することができます.
  • 他の 2 つの通常の RDD は通常どおり結合できます。
  • 最後に、結合演算子を使用して 2 つの結合の結果を組み合わせることができます。これが最終的な結合結果です。
    ジョインによるデータのスキューについて、少数のキーのみがスキューの原因となる場合は、少数のキーを独立した RDD に分割し、ランダムなプレフィックスを追加して n 個の部分に分割して結合することができます.このとき、これらのキーはデータはいくつかのタスクに集中するのではなく、結合のために複数のタスクに分散されます。

7. 結合操作中に、RDD に多数のキーが存在し、データ スキューが発生します。キーを分割しても意味がありません。ランダムなプレフィックスを使用して RDD を拡張します。

  • まず、RDD/Hive テーブルのデータ分布を確認し、データ スキューの原因となっている RDD/Hive テーブルを見つけます (たとえば、複数のキーが 10,000 個を超えるデータに対応する)。
  • 次に、RDD 内の各データは、n 以内のランダムなプレフィックスでマークされます。
  • 同時に、別の通常のRDDの容量を拡張し、各データをn個のデータに拡張し、拡張後の各データに0~nのプレフィックスを追加します。
  • 最後に、2 つの処理済み RDD を結合します。
    分割しない場合は、上記の状況と同等です。

参考文献

「ビッグデータを 5 分で学ぶ - Spark Data Tilt とソリューション」

おすすめ

転載: blog.csdn.net/hshudoudou/article/details/130330153