(Perché) dobbiamo chiamare la cache o persistere su un RDD


171

Quando viene creato un set di dati distribuito (RDD) resiliente da un file di testo o una raccolta (o da un altro RDD), è necessario chiamare "cache" o "persistere" in modo esplicito per memorizzare i dati RDD in memoria? O i dati RDD sono archiviati in modo distribuito nella memoria per impostazione predefinita?

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

Secondo la mia comprensione, dopo il passaggio precedente, textFile è un RDD ed è disponibile in tutta / parte della memoria del nodo.

Se è così, perché allora dobbiamo chiamare "cache" o "persist" su textFile RDD?

Risposte:


300

La maggior parte delle operazioni RDD sono pigre. Pensa a un RDD come una descrizione di una serie di operazioni. Un RDD non è un dato. Quindi questa linea:

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

Non fa niente. Crea un RDD che dice "dovremo caricare questo file". Il file non è caricato a questo punto.

Le operazioni RDD che richiedono l'osservazione del contenuto dei dati non possono essere pigre. (Queste sono chiamate azioni .) Un esempio è RDD.count: per dirti il ​​numero di righe nel file, il file deve essere letto. Quindi, se scrivi textFile.count, a questo punto il file verrà letto, le righe verranno conteggiate e il conteggio verrà restituito.

E se chiami di textFile.countnuovo? La stessa cosa: il file verrà letto e contato di nuovo. Nulla è memorizzato. Un RDD non è un dato.

Quindi cosa fa RDD.cache? Se aggiungi textFile.cacheal codice sopra:

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

Non fa niente. RDD.cacheè anche un'operazione pigra. Il file non è ancora letto. Ma ora il RDD dice "leggi questo file e poi memorizza nella cache i contenuti". Se esegui textFile.countla prima volta, il file verrà caricato, memorizzato nella cache e conteggiato. Se si chiama textFile.countuna seconda volta, l'operazione utilizzerà la cache. Prenderà semplicemente i dati dalla cache e conterà le linee.

Il comportamento della cache dipende dalla memoria disponibile. Se il file non si adatta alla memoria, ad esempio, textFile.counttornerà al solito comportamento e rileggerà il file.


4
Ciao Daniel, - quando chiami cache, significa che il RDD non viene ricaricato dal sorgente (es. File di testo) - come puoi essere sicuro che i dati del file di testo siano più recenti quando sono memorizzati nella cache? (Spark lo capisce o si tratta di un'operazione manuale da non analizzare periodicamente () per garantire che i dati di origine vengano ricalcolati più avanti nella stirpe?)
andrew.butkus

inoltre, se è necessario annullare periodicamente la risposta, se si dispone di un RDD memorizzato nella cache, dipendente da un altro RDD memorizzato nella cache, è necessario annullare la replica di entrambi i RDD per visualizzare i risultati ricalcolati?
andrew.butkus,

21
Spark presuppone che il file non cambierà mai. Legge il file in un momento arbitrario e può rileggerne parti se necessario in un secondo momento. (Ad esempio se un pezzo di dati è stato espulso dalla cache.) Quindi è meglio mantenere invariati i file! Basta creare un nuovo file con un nuovo nome quando si hanno nuovi dati, quindi caricarlo come nuovo RDD. Se ricevi continuamente nuovi dati, guarda Spark Streaming.
Daniel Darabos,

10
Sì. I RDD sono immutabili, quindi ogni RDD presume che anche le sue dipendenze siano immutabili. Spark Streaming consente di impostare tali alberi che operano su un flusso di modifiche. Ma una soluzione ancora più semplice è quella di costruire l'albero in una funzione che accetta un nome file come parametro. Quindi basta chiamare la funzione per il nuovo file e poof, hai il nuovo albero di calcolo.
Daniel Darabos,

1
@Humoyun: nella scheda Archiviazione dell'interfaccia utente Spark è possibile vedere la quantità di ciascun RDD memorizzata nella cache. I dati potrebbero essere così grandi che solo il 40% si adatta alla memoria totale che hai per la memorizzazione nella cache. Un'opzione in questo caso è quella di utilizzare perisiste selezionare un'opzione di archiviazione che consente di versare i dati della cache su disco.
Daniel Darabos,

197

Penso che la domanda sarebbe meglio formulata come:

Quando dobbiamo chiamare la cache o persistere su un RDD?

I processi Spark sono pigri, cioè non accadrà nulla finché non sarà necessario. Per rispondere rapidamente alla domanda, dopo che val textFile = sc.textFile("/user/emp.txt")è stato emesso, non accade nulla ai dati, solo una HadoopRDDviene costruita, usando il file come sorgente.

Diciamo che trasformiamo un po 'quei dati:

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

Ancora una volta, non accade nulla ai dati. Ora c'è un nuovo RDD wordsRDDche contiene un riferimento testFilee una funzione da applicare quando necessario.

Solo quando viene chiamata un'azione su un RDD, come wordsRDD.countla catena RDD, chiamata lignaggio, verrà eseguita. Cioè, i dati, suddivisi in partizioni, verranno caricati dagli esecutori del cluster Spark, la flatMapfunzione verrà applicata e il risultato verrà calcolato.

Su un lignaggio lineare, come quello in questo esempio, cache()non è necessario. I dati verranno caricati sugli esecutori, verranno applicate tutte le trasformazioni e infine countverranno calcolate, tutte in memoria - se i dati si adattano alla memoria.

cacheè utile quando il lignaggio del RDD si dirama. Supponiamo che tu voglia filtrare le parole dell'esempio precedente in un conteggio delle parole positive e negative. Potresti farlo in questo modo:

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

Qui, ogni ramo emette un ricaricamento dei dati. L'aggiunta di un'istruzione esplicita cacheassicurerà che l'elaborazione eseguita in precedenza venga conservata e riutilizzata. Il lavoro sarà simile al seguente:

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()

Per tale motivo, cachesi dice che "interrompa il lignaggio" in quanto crea un checkpoint che può essere riutilizzato per ulteriori elaborazioni.

Regola empirica: utilizzare cachequando il lignaggio del proprio RDD si dirama o quando un RDD viene utilizzato più volte come in un ciclo.


1
Eccezionale. Grazie. Un'altra domanda correlata. Quando memorizziamo nella cache o persistiamo, i dati verranno archiviati nella memoria dell'esecutore o nella memoria del nodo di lavoro. Se è la memoria dell'esecutore, How Spark identifica quale esecutore ha i dati.
Ramana,

1
@RamanaUppala viene utilizzata la memoria dell'esecutore. La frazione della memoria dell'esecutore utilizzata per la memorizzazione nella cache è controllata dalla configurazione spark.storage.memoryFraction. Per quanto riguarda quale esecutore ha quali dati, un RDD terrà traccia delle sue partizioni che sono distribuite sugli esecutori.
martedì

5
@maasg mi corregga se sbaglio, ma non cachene persist riesco a rompere il lignaggio .
zero323,

Dove verrebbero archiviate le parole RD se non avessimo avuto l'istruzione .cache () nell'esempio sopra?
sun_dare,

e se prima che i due contino, uniamo i due rami di nuovo a un rdd e contiamo? in questo caso, la cache è utile?
Xiawei Zhang,

30

Dobbiamo chiamare "cache" o "persistere" in modo esplicito per memorizzare i dati RDD in memoria?

Sì, solo se necessario.

I dati RDD sono archiviati in modo distribuito nella memoria per impostazione predefinita?

No!

E questi sono i motivi per cui:

  • Spark supporta due tipi di variabili condivise: le variabili di trasmissione, che possono essere utilizzate per memorizzare nella cache un valore in memoria su tutti i nodi, e gli accumulatori, che sono variabili che vengono solo "aggiunte", come contatori e somme.

  • Gli RDD supportano due tipi di operazioni: trasformazioni, che creano un nuovo set di dati da uno esistente e azioni, che restituiscono un valore al programma del driver dopo aver eseguito un calcolo sul set di dati. Ad esempio, map è una trasformazione che passa ogni elemento del set di dati attraverso una funzione e restituisce un nuovo RDD che rappresenta i risultati. D'altra parte, ridurre è un'azione che aggrega tutti gli elementi dell'RDD usando una funzione e restituisce il risultato finale al programma del driver (anche se esiste anche un riduttore parallelo che restituisce un set di dati distribuito).

  • Tutte le trasformazioni in Spark sono pigre, in quanto non calcolano immediatamente i loro risultati. Invece, ricordano solo le trasformazioni applicate ad alcuni set di dati di base (ad esempio un file). Le trasformazioni vengono calcolate solo quando un'azione richiede che un risultato sia restituito al programma del driver. Questo design consente a Spark di funzionare in modo più efficiente - ad esempio, possiamo renderci conto che un set di dati creato tramite map verrà utilizzato in una riduzione e restituirà solo il risultato della riduzione al driver, anziché il set di dati mappato più grande.

  • Per impostazione predefinita, ogni RDD trasformato può essere ricalcolato ogni volta che si esegue un'azione su di esso. Tuttavia, è anche possibile mantenere un RDD in memoria usando il metodo persist (o cache), nel qual caso Spark manterrà gli elementi intorno al cluster per un accesso molto più veloce alla successiva interrogazione. Esiste anche il supporto per RDD persistenti su disco o replicati su più nodi.

Per maggiori dettagli, consultare la guida alla programmazione Spark .


1
Questo non ha risposto alla mia domanda.
Ramana,

Cosa non risponde?
eliasah

1
quando i dati di RDD sono archiviati nella memoria predefinita, perché dobbiamo chiamare Cache o Persist?
Ramana,

Gli RDD non sono archiviati in memoria per impostazione predefinita, quindi persistendo nell'RDD Spark esegue la trasformazione più velocemente sul cluster
eliasah

2
È una buona risposta, non so perché sia ​​stato sottoposto a downgrade. È una risposta dall'alto verso il basso, che spiega come funzionano i RDD dai concetti di alto livello. Ho aggiunto un'altra risposta che va dal basso verso l'alto: a partire da "cosa fa questa linea". Forse è più facile seguire qualcuno che ha appena iniziato con Spark.
Daniel Darabos,

11

Di seguito sono riportate le tre situazioni in cui dovresti memorizzare nella cache i tuoi RDD:

usando un RDD più volte

eseguire più azioni sullo stesso RDD

per lunghe catene di (o molto costose) trasformazioni


7

Aggiunta di un altro motivo per aggiungere (o aggiungere temporaneamente) la cachechiamata del metodo.

per problemi di memoria di debug

con il cachemetodo, spark fornirà informazioni di debug relative alle dimensioni dell'RDD. quindi nell'interfaccia utente integrata spark, otterrai informazioni sul consumo di memoria RDD. e questo si è rivelato molto utile per diagnosticare problemi di memoria.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.