Lutin. POINTE :
Chaque fois que vous avez une initialisation lourde qui devrait être effectuée une fois pour de nombreux RDD
éléments plutôt qu'une fois par RDD
élément, et si cette initialisation, telle que la création d'objets à partir d'une bibliothèque tierce, ne peut pas être sérialisée (afin que Spark puisse la transmettre à travers le cluster à les nœuds worker), utilisez à la mapPartitions()
place de
map()
. mapPartitions()
prévoit que l'initialisation doit être effectuée une fois par tâche de travail / thread / partition au lieu d'une fois par RDD
élément de données par exemple: voir ci-dessous.
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. ne flatMap
se comporte comme une carte ou comme mapPartitions
?
Oui. s'il vous plaît voir l'exemple 2 de flatmap
.. son explicite.
Q1. Quelle est la différence entre un RDD map
etmapPartitions
map
exécute la fonction utilisée au niveau de chaque élément tout en l'
mapPartitions
exerçant au niveau de la partition.
Exemple de scénario : si nous avons 100K éléments dans uneRDD
partitionparticulière,nous déclencherons la fonction utilisée par la transformation de mappage 100K fois lorsque nous l'utilisonsmap
.
Inversement, si nous utilisons, mapPartitions
nous n'appellerons la fonction particulière qu'une seule fois, mais nous passerons tous les enregistrements de 100K et récupérerons toutes les réponses en un seul appel de fonction.
Il y aura un gain de performance puisque map
travaille sur une fonction particulière tant de fois, surtout si la fonction fait quelque chose de cher à chaque fois qu'elle n'aurait pas besoin de faire si nous passions tous les éléments à la fois (dans le cas de mappartitions
).
carte
Applique une fonction de transformation à chaque élément du RDD et renvoie le résultat sous forme de nouveau RDD.
Liste des variantes
Def map [U: ClassTag] (f: T => U): RDD [U]
Exemple :
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
Il s'agit d'une carte spécialisée qui n'est appelée qu'une seule fois pour chaque partition. L'ensemble du contenu des partitions respectives est disponible sous forme de flux séquentiel de valeurs via l'argument d'entrée (Iterarator [T]). La fonction personnalisée doit renvoyer un autre Iterator [U]. Les itérateurs de résultats combinés sont automatiquement convertis en un nouveau RDD. Veuillez noter que les tuples (3,4) et (6,7) sont absents du résultat suivant en raison du partitionnement que nous avons choisi.
preservesPartitioning
indique si la fonction d'entrée préserve le partitionneur, ce qui devrait l'être false
sauf s'il s'agit d'une paire RDD et que la fonction d'entrée ne modifie pas les touches.
Liste des variantes
def mapPartitions [U: ClassTag] (f: Iterator [T] => Iterator [U], préserve le partitionnement: Boolean = false): RDD [U]
Exemple 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))
Exemple 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)
Le programme ci-dessus peut également être écrit en utilisant flatMap comme suit.
Exemple 2 avec 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)
Conclusion :
mapPartitions
la transformation est plus rapide que map
puisqu'elle appelle votre fonction une fois / partition, pas une fois / élément.
Lectures complémentaires: foreach Vs foreachPartitions Quand utiliser Quoi?