曝光 小费 :
每当您进行重量级初始化时,应对多个RDD
元素进行一次初始化,而不是对每个RDD
元素进行一次,并且这种初始化(例如从第三方库创建对象)不能被序列化(以便Spark可以跨集群将其传输到工作节点),请使用mapPartitions()
代替
map()
。mapPartitions()
提供了每个工作任务/线程/分区执行一次初始化的操作,而不是每个RDD
数据元素执行一次的初始化操作:例如,请参见下文。
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。不flatMap
循规蹈矩像地图或类似mapPartitions
?
是。请参阅示例2的flatmap
说明。
Q1。RDD map
和RDD有什么区别mapPartitions
map
在每个元素级别上
mapPartitions
工作的功能,而在分区级别上工作的功能。
示例场景: 如果在特定RDD
分区中有100K个元素,则在使用时,将触发映射转换使用的函数100K次map
。
相反,如果使用,mapPartitions
那么我们只会调用一次特定的函数,但是我们将传递所有100K记录,并在一次函数调用中获取所有响应。
由于map
在一个特定函数上工作了很多次,因此性能将得到提高,尤其是如果函数每次都执行昂贵的工作,而如果一次传递所有元素(在的情况下mappartitions
)则不需要这样做。
地图
在RDD的每个项目上应用转换函数,并将结果作为新的RDD返回。
列表变体
def map [U:ClassTag](f:T => U):RDD [U]
范例:
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
这是一个专门的映射,每个分区仅被调用一次。各个分区的全部内容可通过输入参数(Iterarator [T])作为值的顺序流使用。自定义函数必须返回另一个Iterator [U]。合并的结果迭代器将自动转换为新的RDD。请注意,由于选择的分区,以下结果缺少元组(3,4)和(6,7)。
preservesPartitioning
指示输入函数是否保留分区程序,false
除非这是一对RDD并且输入函数不修改键,否则应该保留。
列表变体
def mapPartitions [U:ClassTag](f:Iterator [T] => Iterator [U],preservesPartitioning:Boolean = false):RDD [U]
例子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))
例子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)
上面的程序也可以使用flatMap编写,如下所示。
示例2使用平面图
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)
结论:
mapPartitions
转换比map
调用函数一次/分区而不是一次/元素快得多。
进一步阅读:foreach与foreachPartitions何时使用什么?