web-dev-qa-db-ja.com

合体に最適なnumberOfPartitionsを計算する方法は?

したがって、一般的に、次の場合にcoalesce()を使用する必要があることを理解しています。

filterまたはその他の操作により、パーティションの数が減少し、元のデータセット(RDD、DF)が減少する可能性があります。 coalesce()は、大きなデータセットをフィルタリングした後、操作をより効率的に実行するのに役立ちます。

また、必要な場合にのみデータを移動することでシャッフルを減らすため、repartitionよりも安価であることも理解しています。私の問題は、coalesceが取るパラメーター(idealPartionionNo)をどのように定義するかです。私は別のエンジニアから渡されたプロジェクトに取り組んでおり、彼は以下の計算を使用してそのパラメーターの値を計算していました。

// DEFINE OPTIMAL PARTITION NUMBER
implicit val NO_OF_EXECUTOR_INSTANCES = sc.getConf.getInt("spark.executor.instances", 5)
implicit val NO_OF_EXECUTOR_CORES = sc.getConf.getInt("spark.executor.cores", 2)

val idealPartionionNo = NO_OF_EXECUTOR_INSTANCES * NO_OF_EXECUTOR_CORES * REPARTITION_FACTOR

次に、これはpartitionerオブジェクトで使用されます。

val partitioner = new HashPartitioner(idealPartionionNo)

と一緒に使用されます:

RDD.filter(x=>x._3<30).coalesce(idealPartionionNo)

これは正しいアプローチですか? idealPartionionNo値の計算の背後にある主なアイデアは何ですか? REPARTITION_FACTORとは何ですか?それを定義するために一般的にどのように作業しますか?

また、YARNはオンザフライで利用可能なエグゼキュータを識別する責任があるので、その番号(AVAILABLE_EXECUTOR_INSTANCES)をオンザフライで取得し、それをidealPartionionNoの計算に使用する方法はありますか(つまり、NO_OF_EXECUTOR_INSTANCESAVAILABLE_EXECUTOR_INSTANCESに置き換えます)?

理想的には、フォームのいくつかの実際の例:

  • ここにあるデータセットサイズ);
  • これは、RDD/DFのいくつかの変換と可能な再利用です。
  • ここで、再パーティション化/合体する必要があります。
  • nexecutors with mcores and partition factor equal tokがあるとします。

その後:

  • パーティションの理想的な数は==> ???

また、これらを説明している素敵なブログを紹介していただければ幸いです。

16

実際には、最適なパーティション数は、使用可能なリソースよりも、使用するデータ、使用する変換、および全体的な構成に大きく依存します。

  • パーティションの数が少なすぎると、GCの一時停止が長くなり、さまざまな種類のメモリの問題が発生し、最後にリソース使用率が最適ではなくなります。
  • パーティションの数が多すぎると、メンテナンスコストが処理コストを簡単に超える可能性があります。さらに、非分散削減操作(reduceとは対照的にtreeReduceなど)を使用する場合、パーティションの数が多いと、ドライバーの負荷が高くなります。

コアの数と比較してパーティションをオーバーサブスクライブすること(ファクター2または3が一般的であるようです)、またはパーティションを特定のサイズに保つことを提案するいくつかのルールを見つけることができますが、これは独自のコードを考慮していません:

  • たくさん割り当てると、GCの一時停止が長くなることが予想されるため、パーティションを小さくする方がよいでしょう。
  • 特定のコードが高価な場合、シャッフルコストはより高い同時実行性によって償却できます。
  • フィルタがある場合は、述語の識別力に基づいてパーティションの数を調整できます(5%のデータと99%のデータを保持する場合は、さまざまな決定を行います)。

私の考えでは:

  • 1回限りのジョブでは、安全側にとどまるために多数のパーティションを保持します(失敗するよりも遅い方が良いです)。
  • 再利用可能なジョブでは、保守的な構成で開始し、実行-監視-構成の調整-繰り返します。
  • エグゼキュータまたはコアの数に基づいて、固定数のパーティションを使用しようとしないでください。最初にデータとコードを理解し、次に理解を反映するように構成を調整します。

    通常、クラスターが安定した動作を示すパーティションごとの生データの量を決定するのは比較的簡単です(私の経験では、データのロードに使用する形式、データ構造に応じて、数百メガバイトの範囲になります。および構成)。これはあなたが探している「マジックナンバー」です。

一般的に覚えておく必要のあるいくつかのこと:

  • パーティションの数は、必ずしもデータの分散を反映しているわけではありません。シャッフルが必要な操作(*byKeyjoinRDD.partitionByDataset.repartition)データの分散が不均一になる可能性があります。重大なデータスキューの症状がないか、常にジョブを監視してください。
  • 一般に、パーティションの数は一定ではありません。複数の依存関係(unioncoGroupjoin)を持つ操作は、パーティションの数に影響を与える可能性があります。
15
zero323

あなたの質問は有効ですが、Sparkパーティショニングの最適化は実行中の計算に依存します完全に。再パーティショニング/合体する正当な理由が必要です。 RDDを数えているだけで(まばらに配置されたパーティションが大量にある場合でも)、再パーティション化/合体の手順を実行すると速度が低下します。

再分割と合体

repartition(n)coalesce(n, shuffle = true)coalesce(n, shuffle = false)と同じ)の違いは、実行モデルと関係があります。シャッフルモデルは、元のRDDの各パーティションをランダムに取得します。データをすべてのエグゼキュータに送信し、新しい(少ないまたは多い)数のパーティションを持つRDDを生成します。シャッフルなしモデルは、複数のパーティションを1つのタスクとしてロードする新しいRDDを作成します。

この計算について考えてみましょう。

sc.textFile("massive_file.txt")
  .filter(sparseFilterFunction) // leaves only 0.1% of the lines
  .coalesce(numPartitions, shuffle = shuffle)

shuffletrueの場合、テキストファイル/フィルターの計算はtextFileのデフォルトで指定されたいくつかのタスクで行われ、フィルター処理された小さな結果がシャッフルされます。 shufflefalseの場合、タスクの総数は最大でnumPartitionsになります。

numPartitionsが1の場合、違いは非常に大きくなります。シャッフルモデルは、データを並行して処理およびフィルタリングし、フィルタリングされた結果の0.1%をダウンストリームDAG操作のために1つのエグゼキュータに送信します。シャッフルなしモデルは、最初からすべて1つのコアでデータを処理およびフィルタリングします。

実行する手順

ダウンストリーム操作を検討してください。このデータセットを1回だけ使用している場合は、おそらく再パーティション化する必要はまったくありません。後で使用するために(たとえばディスクに)フィルタリングされたRDDを保存する場合は、上記のトレードオフを考慮してください。これらのモデルに慣れるには経験が必要であり、パフォーマンスが向上すると、両方を試して、パフォーマンスを確認してください。

8
Tim

他の人が答えたように、あなたが求めるものを計算する公式はありません。そうは言っても、最初の部分について知識に基づいて推測し、時間をかけて微調整することができます。

最初のステップは、十分なパーティションがあることを確認することです。 NO_OF_EXECUTOR_INSTANCESエグゼキューターとエグゼキューターごとのNO_OF_EXECUTOR_CORESコアがある場合は、NO_OF_EXECUTOR_INSTANCES * NO_OF_EXECUTOR_CORESパーティションを同時に処理できます(それぞれが特定のインスタンスの特定のコアに移動します)。つまり、これはすべてがコア間で均等に分割され、すべてが処理にまったく同じ時間を要することを前提としています。これはめったにありません。局所性(たとえば、データが別のノードから取得する必要がある)のため、または単にバランスが取れていないため(たとえば、データがルートドメインでパーティション化されている場合、グーグルはおそらくかなり大きいでしょう)。ここでREPARTITION_FACTORが役立ちます。アイデアは、各コアを「オーバーブッキング」することです。したがって、1つが非常に速く終了し、もう1つがゆっくり終了する場合、タスクをそれらの間で分割するオプションがあります。一般に、2〜3の因数は良い考えです。

次に、単一のパーティションのサイズを見てみましょう。データ全体のサイズがXMBで、N個のパーティションがあるとします。各パーティションは平均X/NMBになります。 NがXに比べて大きい場合、平均パーティションサイズが非常に小さい可能性があります(たとえば、数KB)。この場合、各パーティションの管理のオーバーヘッドが高くなりすぎるため、通常はNを低くすることをお勧めします。一方、サイズが非常に大きい場合(たとえば、数GB)、同時に大量のデータを保持する必要があり、ガベージコレクション、メモリ使用量の増加などの問題が発生します。

最適なサイズは良い質問ですが、一般的に人々は100〜1000 MBのパーティションを好むようですが、実際には数十MBもおそらく良いでしょう。

注意すべきもう1つの点は、パーティションがどのように変化するかを計算するときです。たとえば、それぞれ100MBの1000パーティションから始めて、各パーティションが1Kになるようにデータをフィルタリングすると、おそらく合体する必要があります。 groupbyを実行したり、参加したりすると、同様の問題が発生する可能性があります。このような場合、パーティションのサイズとパーティションの数の両方が変化し、望ましくないサイズに達する可能性があります。

3
Assaf Mendelson