私は次のsparkジョブを持っています。すべてをメモリに保持しようとしています:
val myOutRDD = myInRDD.flatMap { fp =>
val Tuple2List: ListBuffer[(String, myClass)] = ListBuffer()
:
Tuple2List
}.persist(StorageLevel.MEMORY_ONLY).reduceByKey { (p1, p2) =>
myMergeFunction(p1,p2)
}.persist(StorageLevel.MEMORY_ONLY)
しかし、ジョブトラッカーを調べたとき、まだディスクへのShuffle WriteとShuffleの流出がたくさんあります...
Total task time across all tasks: 49.1 h
Input Size / Records: 21.6 GB / 102123058
Shuffle write: 532.9 GB / 182440290
Shuffle spill (memory): 370.7 GB
Shuffle spill (disk): 15.4 GB
その後、"no space left on device"
... 532.9 GBシャッフル書き込みここで、ディスクまたはメモリに書き込まれますか?
また、メモリに保持するように具体的に要求しているときに、まだ15.4 Gのデータがディスクに流出しているのはなぜですか?
ありがとう!
シャッフルスピル(メモリ)は、スピルしたときのメモリ内のデータのデシリアライズされた形式のサイズです。一方、シャッフルスピル(ディスク)は、流出後のディスク上のデータのシリアル化された形式。これが、後者が前者よりもはるかに小さくなる傾向がある理由です。両方のメトリックは、タスクの全期間にわたって集計されていることに注意してください(つまり、各タスク内で複数回こぼすことができます)。
RDDに複数回アクセスしないと、コード内のpersist
呼び出しは完全に無駄になります。アクセスしない場合、何かを保存する意味は何ですか?キャッシングはシャッフルの動作には影響しませんが、シャッフルの出力をキャッシュに保持することでシャッフルの再実行を回避できます。
シャッフル流出は spark.shuffle.spill
およびspark.shuffle.memoryFraction
構成パラメーター。 spill
が有効になっている場合(デフォルト)、memoryFraction
(デフォルトでは20%)よりも多くのファイルを使用し始めると、シャッフルファイルはディスクに流出します。
メトリックは非常に紛らわしいです。 code の私の読みは、 "シャッフル流出(メモリ)"は、物として解放されたメモリの量ですディスクにこぼれた。 code for "Shuffle spill(disk)"は、実際にディスクに書き込まれた量のように見えます。 コード for "Shuffle write"ソーターからの流出としてではなく、ディスクに直接書き込まれた量だと思う。
シャッフルの流出を防ぐ方法に関するもう1つの注意点は、パフォーマンスの観点からの質問の中で最も重要な部分だと思います(上記のシャッフルの書き込みはシャッフルの必須部分です)。
シャッフルが読み込まれたときにスピルが発生した場合、レデューサーはそれに割り当てられたすべてのレコードをそのエグゼキューターのシャッフルスペースのメモリに収めることができません。シャッフルがアンバランスな場合(たとえば、一部の出力パーティションが一部の入力パーティションよりもはるかに大きい場合)、シャッフルの前にパーティションが「メモリに収まる」場合でもシャッフルが流出する可能性があります。これを制御する最良の方法は、A)シャッフルのバランスをとることです。たとえば、シャッフルする前に、または異なるキーでシャッフルすることによりコードを変更するか、B)上記の提案に従ってシャッフルメモリ設定を変更します。おそらくBではなくAを行う必要があります。
シャッフル書き込みとは、一時キャッシュの場所でローカルファイルシステムに書き込まれたデータを意味します。糸クラスターモードでは、このプロパティをyarn-site.xmlの属性「yarn.nodemanager.local-dirs」で設定できます。そのため、「シャッフル書き込み」とは、一時的な場所に書き込んだデータのサイズを意味します。 「シャッフル流出」は、シャッフルステージの結果である可能性が高くなります。とにかく、それらの数字は蓄積されます。