(なぜ)RDDでキャッシュを呼び出すか、永続化する必要があるか


171

弾力性のある分散データセット(RDD)がテキストファイルまたはコレクション(または別のRDD)から作成された場合、RDDデータをメモリに格納するために「キャッシュ」または「永続化」を明示的に呼び出す必要がありますか?または、RDDデータはデフォルトでメモリに分散して保存されますか?

val textFile = sc.textFile("/user/emp.txt")

私の理解では、上記の手順の後、textFileはRDDであり、ノードのメモリのすべてまたは一部で使用できます。

もしそうなら、なぜtextFile RDDで「キャッシュ」または「持続」を呼び出す必要があるのですか?

回答:


300

ほとんどのRDD操作は遅延です。RDDを一連の操作の説明と考えてください。RDDはデータではありません。したがって、この行:

val textFile = sc.textFile("/user/emp.txt")

何もしません。「このファイルをロードする必要があります」というRDDを作成します。この時点ではファイルはロードされていません。

データの内容を監視する必要があるRDD操作は、遅延することはできません。(これらはアクションと呼ばれます。)例はRDD.count、ファイルの行数を通知するために、ファイルを読み取る必要があります。したがって、を書き込むtextFile.countと、この時点でファイルが読み取られ、行がカウントされ、カウントが返されます。

textFile.countもう一度電話したらどうなる?同じこと:ファイルが読み込まれ、再度カウントされます。何も保存されていません。RDDはデータではありません。

それで、何をしRDD.cacheますか?textFile.cache上記のコードに追加すると:

val textFile = sc.textFile("/user/emp.txt")
textFile.cache

何もしません。RDD.cacheまた、遅延操作です。ファイルはまだ読み取られていません。しかし、RDDは「このファイルを読み取り、内容をキャッシュする」と言っています。その後textFile.count初めて実行すると、ファイルがロードされ、キャッシュされ、カウントされます。もう一度呼び出すtextFile.countと、操作はキャッシュを使用します。キャッシュからデータを取得し、行を数えるだけです。

キャッシュの動作は、使用可能なメモリによって異なります。たとえば、ファイルがメモリに収まらない場合、textFile.count通常の動作に戻り、ファイルを再度読み取ります。


4
こんにちはダニエル-キャッシュを呼び出すとき、これはRDDがソース(テキストファイルなど)からリロードされないことを意味します-キャッシュされているときにテキストファイルのデータが最新であることをどのように確認できますか?(sparkはこれを理解しますか、それともソースデータが系統の後で再計算されるようにするために定期的にunpersist()を実行する手動操作ですか?)
andrew.butkus '19年

また-定期的に非永続化する必要がある場合-キャッシュされているrddがあり、キャッシュされている別のRDDに依存している場合、再計算された結果を表示するには、両方のRDDを非永続化する必要がありますか?
andrew.butkus 2015年

21
Sparkは、ファイルが変更されないことを前提としています。任意の時点でファイルを読み取り、後で必要に応じてその一部を再度読み取る可能性があります。(たとえば、データの一部がキャッシュから押し出された場合。)したがって、ファイルを変更しないようにすることをお勧めします。新しいデータがある場合は、新しい名前で新しいファイルを作成し、新しいRDDとしてロードします。継続的に新しいデータを取得している場合は、Spark Streamingを調べてください。
Daniel Darabos、2015年

10
はい。RDDは不変であるため、すべてのRDDはその依存関係も不変であると想定しています。Spark Streamingを使用すると、変更のストリームを操作するこのようなツリーを設定できます。しかし、さらに簡単な解決策は、ファイル名をパラメーターとして取る関数でツリーを構築することです。次に、新しいファイルとpoofの関数を呼び出すだけで、新しい計算ツリーが得られます。
Daniel Darabos、2015年

1
@Humoyun:Spark UIの[ストレージ]タブで、各RDDがどれだけキャッシュされているかを確認できます。データが非常に大きくなる可能性があるため、40%しかキャッシュ用の合計メモリに収まりません。この場合の1つのオプションはperisist、キャッシュデータをディスクに書き出すことができるストレージオプションを使用して選択することです。
ダニエルダラボス2016

197

質問は次のように定式化されると思います。

キャッシュを呼び出すか、RDDで永続化する必要があるのはいつですか?

Sparkプロセスはレイジーです。つまり、必要になるまで何も起こりません。質問にすばやく答えるために、val textFile = sc.textFile("/user/emp.txt")発行後、データには何も起こりませんHadoopRDD。ファイルをソースとして使用して、aのみが構築されます。

そのデータを少し変換するとします。

val wordsRDD = textFile.flatMap(line => line.split("\\W"))

繰り返しますが、データには何も起こりません。これで、必要なときに適用される関数へのwordsRDD参照testFileと関数を含む新しいRDD ができました。

系統wordsRDD.countなどのRDDチェーンなどのRDDに対してアクションが呼び出された場合のみ実行されます。つまり、パーティションに分割されたデータは、Sparkクラスターのエグゼキューターによってロードされ、関数が適用され、結果が計算されます。flatMap

この例のような線形の系統でcache()は必要ありません。データはエグゼキュータにロードされ、すべての変換が適用され、最後countにすべてがメモリ内で計算されます-データがメモリに収まる場合。

cacheRDDの系統が分岐する場合に役立ちます。前の例の単語をフィルター処理して、肯定的な単語と否定的な単語のカウントにしたいとします。あなたはそのようにすることができます:

val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

ここでは、各ブランチがデータのリロードを発行します。明示的なcacheステートメントを追加すると、以前に実行された処理が確実に保持され、再利用されます。ジョブは次のようになります。

val textFile = sc.textFile("/user/emp.txt")
val wordsRDD = textFile.flatMap(line => line.split("\\W"))
wordsRDD.cache()
val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()

そのため、cache今後の処理に再利用できるチェックポイントが作成されるため、「系統を壊す」と言われています。

経験則:cacheRDDの系統が分岐する場合、またはRDDがループのように複数回使用される場合に使用します。


1
驚くばかり。ありがとう。関連するもう1つの質問。キャッシュまたは永続化すると、データはエグゼキューターのメモリまたはワーカーノードのメモリに格納されます。エグゼキューターのメモリの場合、How Sparkはどのエグゼキューターがデータを持っているかを識別します。
ラマナ2015年

1
@RamanaUppalaエグゼキュータメモリが使用されます。キャッシングに使用されるエグゼキューターメモリの割合は、configによって制御されspark.storage.memoryFractionます。どのエグゼキューターがどのデータを持っているかに関して、RDDはエグゼキューターに分散されているパーティションを追跡します。
maasg 2015年

5
私はどちらも間違っている場合@maasgは私を修正しcacheたりpersist することができ系譜を破ります
zero323 2016

上記の例で.cache()ステートメントがない場合、wordsRDDはどこに保存されますか?
sun_dare 2017

2つのカウントの前に、2つのブランチを結合して1つのrddに戻し、カウントするとどうなるでしょうか。この場合、キャッシュは有益ですか?
Xiawei Zhang 2018

30

RDDデータをメモリに保存するために、「キャッシュ」または「持続」を明示的に呼び出す必要がありますか?

はい、必要な場合のみ。

RDDデータはデフォルトでメモリに分散して保存されますか?

番号!

そして、これらは理由です:

  • Sparkは2種類の共有変数をサポートします。すべてのノードのメモリに値をキャッシュするために使用できるブロードキャスト変数と、カウンターや合計など、「追加」されるだけの変数であるアキュムレーターです。

  • RDDは、既存のデータセットから新しいデータセットを作成する変換と、データセットで計算を実行した後にドライバープログラムに値を返すアクションの2種類の操作をサポートしています。たとえば、mapは、各データセット要素を関数に渡し、結果を表す新しいRDDを返す変換です。一方、reduceは、何らかの関数を使用してRDDのすべての要素を集約し、ドライバープログラムに最終結果を返すアクションです(分散データセットを返す並列のreduceByKeyもあります)。

  • Sparkのすべての変換は、結果をすぐに計算しないという点で、遅延です。代わりに、ベースデータセット(ファイルなど)に適用された変換を覚えているだけです。変換は、アクションが結果をドライバープログラムに返す必要がある場合にのみ計算されます。この設計により、Sparkをより効率的に実行できるようになります。たとえば、マップを通じて作成されたデータセットは削減で使用され、大きなマップされたデータセットではなく、削減の結果のみをドライバーに返すことがわかります。

  • デフォルトでは、変換された各RDDは、アクションを実行するたびに再計算される場合があります。ただし、persist(またはキャッシュ)メソッドを使用してRDDをメモリに永続化することもできます。その場合、Sparkは、次にクエリするときにはるかに高速にアクセスできるように、クラスター上の要素を維持します。また、RDDをディスク上に永続化したり、複数のノード間で複製したりすることもできます。

詳細については、Sparkプログラミングガイドを確認してください。


1
それは私の質問に答えませんでした。
ラマナ2015年

何が答えないのですか?
エリアサ

1
RDDのデータがメモリのデフォルトに格納されている場合、なぜCacheまたはPersistを呼び出す必要があるのですか?
ラマナ

RDDはデフォルトではメモリに保存されないため、RDDを永続化すると、クラスターでSparkが変換をより速く実行できるようになります
eliasah

2
それは良い答えです、なぜそれが反対投票されたのか分かりません。これはトップダウンの回答であり、RDDが高レベルの概念からどのように機能するかを説明します。「この行は何をするのか」から始めて、ボトムアップからの別の回答を追加しました。たぶん、Sparkを使い始めたばかりの人にとっては、従う方が簡単かもしれません。
Daniel Darabos、2015年

11

以下は、RDDをキャッシュする必要がある3つの状況です。

RDDを何度も使用する

同じRDDで複数のアクションを実行する

変換の長いチェーン(または非常に高価な)の場合


7

cacheメソッド呼び出しを追加する(または一時的に追加する)別の理由を追加します。

デバッグメモリの問題

cache方法、スパークは、RDDのサイズに関するデバッグ情報を与えます。そのため、spark統合UIでは、RDDメモリ消費情報を取得します。これは、メモリの問題を診断するのに非常に役立ちました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.