Imp. MANCIA :
Ogni volta che si dispone di un'inizializzazione pesante che dovrebbe essere eseguita una volta per molti RDD
elementi anziché una volta per RDD
elemento e se questa inizializzazione, come la creazione di oggetti da una libreria di terze parti, non può essere serializzata (in modo che Spark possa trasmetterla attraverso il cluster a i nodi di lavoro), utilizzare mapPartitions()
invece di
map()
. mapPartitions()
prevede che l'inizializzazione venga eseguita una volta per attività / thread / partizione di lavoro anziché una volta RDD
per esempio per elemento dati : vedere di seguito.
val newRd = myRdd.mapPartitions(partition => {
val connection = new DbConnection /*creates a db connection per partition*/
val newPartition = partition.map(record => {
readMatchingFromDB(record, connection)
}).toList // consumes the iterator, thus calls readMatchingFromDB
connection.close() // close dbconnection here
newPartition.iterator // create a new iterator
})
Q2. si flatMap
comporta come una mappa o come mapPartitions
?
Sì. vedere l'esempio 2 di flatmap
... si spiega da sé.
Q1. Qual è la differenza tra un RDD map
emapPartitions
map
funziona la funzione utilizzata a livello di elemento mentre
mapPartitions
esercita la funzione a livello di partizione.
Scenario di esempio : se abbiamo 100K elementi in unaRDD
partizioneparticolare, disattiveremo la funzione utilizzata dalla trasformazione della mappatura 100K volte quando usiamomap
.
Al contrario, se lo utilizziamo mapPartitions
, chiameremo la funzione specifica una sola volta, ma passeremo in tutti i record da 100K e otterremo tutte le risposte in una chiamata di funzione.
Ci sarà un miglioramento delle prestazioni poiché map
funziona su una particolare funzione così tante volte, specialmente se la funzione sta facendo qualcosa di costoso ogni volta che non avrebbe bisogno di fare se passassimo tutti gli elementi contemporaneamente (in caso di mappartitions
).
carta geografica
Applica una funzione di trasformazione su ciascun elemento del RDD e restituisce il risultato come nuovo RDD.
Elenco delle varianti
def map [U: ClassTag] (f: T => U): RDD [U]
Esempio :
val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
val b = a.map(_.length)
val c = a.zip(b)
c.collect
res0: Array[(String, Int)] = Array((dog,3), (salmon,6), (salmon,6), (rat,3), (elephant,8))
mapPartitions
Questa è una mappa specializzata che viene chiamata una sola volta per ogni partizione. L'intero contenuto delle rispettive partizioni è disponibile come flusso sequenziale di valori tramite l'argomento input (Iterarator [T]). La funzione personalizzata deve restituire ancora un altro Iteratore [U]. Gli iteratori di risultati combinati vengono automaticamente convertiti in un nuovo RDD. Si noti che le tuple (3,4) e (6,7) mancano dal seguente risultato a causa del partizionamento che abbiamo scelto.
preservesPartitioning
indica se la funzione di input preserva il partizionatore, il che dovrebbe essere a false
meno che questa non sia una coppia RDD e la funzione di input non modifichi le chiavi.
Elenco delle varianti
def mapPartitions [U: ClassTag] (f: Iterator [T] => Iterator [U], preservesPartitioning: Boolean = false): RDD [U]
Esempio 1
val a = sc.parallelize(1 to 9, 3)
def myfunc[T](iter: Iterator[T]) : Iterator[(T, T)] = {
var res = List[(T, T)]()
var pre = iter.next
while (iter.hasNext)
{
val cur = iter.next;
res .::= (pre, cur)
pre = cur;
}
res.iterator
}
a.mapPartitions(myfunc).collect
res0: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8))
Esempio 2
val x = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10), 3)
def myfunc(iter: Iterator[Int]) : Iterator[Int] = {
var res = List[Int]()
while (iter.hasNext) {
val cur = iter.next;
res = res ::: List.fill(scala.util.Random.nextInt(10))(cur)
}
res.iterator
}
x.mapPartitions(myfunc).collect
// some of the number are not outputted at all. This is because the random number generated for it is zero.
res8: Array[Int] = Array(1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 7, 7, 7, 9, 9, 10)
Il programma sopra può anche essere scritto usando flatMap come segue.
Esempio 2 usando flatmap
val x = sc.parallelize(1 to 10, 3)
x.flatMap(List.fill(scala.util.Random.nextInt(10))(_)).collect
res1: Array[Int] = Array(1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10)
Conclusione :
mapPartitions
la trasformazione è più veloce di map
quando chiama la tua funzione una volta / partizione, non una volta / elemento ..
Ulteriori letture: foreach Vs foreachPartitions Quando usare cosa?