曝光 小费 :
  每当您进行重量级初始化时,应对多个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何时使用什么?