Diablillo. PROPINA :
Siempre que tenga una inicialización de peso pesado que se debe hacer una vez para muchos RDD
elementos en lugar de una vez por RDD
elemento, y si esta inicialización, como la creación de objetos de una biblioteca de terceros, no se puede serializar (para que Spark pueda transmitirla a través del clúster a los nodos de trabajo), use en mapPartitions()
lugar de
map()
. mapPartitions()
prevé que la inicialización se realice una vez por tarea de trabajo / hilo / partición en lugar de una vez por RDD
elemento de datos, por ejemplo: ver más abajo
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 se flatMap
comporta como mapa o como mapPartitions
?
Si. vea el ejemplo 2 de flatmap
... se explica por sí mismo.
Q1. ¿Cuál es la diferencia entre un RDD map
ymapPartitions
map
funciona la función que se utiliza en un nivel por elemento, mientras que
mapPartitions
ejerce la función en el nivel de partición.
Escenario de ejemplo : si tenemos 100K elementos en unaRDD
particiónparticular,activaremos la función que está siendo utilizada por la transformación de mapeo 100K veces cuando la usemosmap
.
Por el contrario, si usamos mapPartitions
, solo llamaremos a la función particular una vez, pero pasaremos todos los registros de 100K y recuperaremos todas las respuestas en una llamada de función.
Habrá aumento de rendimiento ya que map
funciona en una función en particular tantas veces, especialmente si la función está haciendo algo costoso cada vez que no tendría que hacerlo si pasamos todos los elementos a la vez (en caso de mappartitions
).
mapa
Aplica una función de transformación en cada elemento del RDD y devuelve el resultado como un nuevo RDD.
Listado de variantes
mapa de definición [U: ClassTag] (f: T => U): RDD [U]
Ejemplo:
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
Este es un mapa especializado que se llama solo una vez para cada partición. El contenido completo de las particiones respectivas está disponible como una secuencia secuencial de valores a través del argumento de entrada (Iterarator [T]). La función personalizada debe devolver otro iterador [U]. Los iteradores de resultados combinados se convierten automáticamente en un nuevo RDD. Tenga en cuenta que las tuplas (3,4) y (6,7) faltan en el siguiente resultado debido a la partición que elegimos.
preservesPartitioning
indica si la función de entrada conserva el particionador, lo que debería ser a false
menos que sea un par RDD y la función de entrada no modifique las teclas.
Listado de variantes
def mapPartitions [U: ClassTag] (f: Iterator [T] => Iterator [U], conservaPartitions: Boolean = false): RDD [U]
Ejemplo 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))
Ejemplo 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)
El programa anterior también se puede escribir usando flatMap de la siguiente manera.
Ejemplo 2 usando mapa plano
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)
Conclusión
mapPartitions
la transformación es más rápida que map
ya que llama a su función una vez / partición, no una vez / elemento ...
Lecturas adicionales: foreach Vs foreachPartitions Cuándo usar ¿Qué?