Apache Spark:map vs mapPartitions?

时间:2014-01-17 11:41:12

标签: performance scala apache-spark rdd

RDD's mapmapPartitions方法之间的区别是什么? flatMap的行为是map还是mapPartitions?感谢。

(编辑)

之间的差异(在语义上或在执行方面)
  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.mapPartitions({ iter: Iterator[A] => for (i <- iter) yield fn(i) },
      preservesPartitioning = true)
  }

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.map(fn)
  }

4 个答案:

答案 0 :(得分:103)

  

RDD的map和mapPartitions方法有什么区别?

方法map通过应用函数将源RDD的每个元素转换为结果RDD的单个元素。 mapPartitions将源RDD的每个分区转换为结果的多个元素(可能没有)。

  

flatMap的行为是map还是mapPartitions?

flatMap都不对单个元素(map)起作用,并生成结果的多个元素(mapPartitions)。

答案 1 :(得分:90)

进出口。提示:

  

每当你进行重量级初始化时,应该进行一次   对于许多RDD元素,而不是每个RDD元素一次,如果是这样的话   初始化,例如从第三方创建对象   库,无法序列化(以便Spark可以传输它   群集到工作节点),使用mapPartitions()代替   map()mapPartitions()提供了要完成的初始化   每个工作任务/线程/分区一次,而不是每RDD个数据一次   example :的元素见下文。

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

是。请参阅flatmap的示例2 ..其自我解释。

  

Q1。 RDD的mapmapPartitions

之间的区别是什么?      

map用于在每个元素级别使用的函数   mapPartitions在分区级别执行该功能。

示例场景如果我们在特定RDD分区中有100K个元素,那么我们将触发映射转换使用的函数100K次我们使用map

相反,如果我们使用mapPartitions,那么我们只会调用一次特定函数,但是我们将传入所有100K记录并在一次函数调用中返回所有响应。

由于map对特定函数的工作次数很多次会有性能提升,特别是如果函数在每次传入时都不需要执行某些操作就会很昂贵所有元素一次(如果mappartitions)。

地图

  

在RDD的每个项目上应用转换函数并返回   结果作为新的RDD。

     

列出变体

     

def map [U:ClassTag](f:T =&gt; 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

  

这是一个专门的地图,每个分区只调用一次。   各个分区的全部内容可用作a   通过输入参数(Iterarator [T])连续的值流。   自定义函数必须返回另一个迭代器[U]。合并   结果迭代器会自动转换为新的RDD。请   请注意,以下缺少元组(3,4)和(6,7)   结果是由于我们选择的分区。

     

preservesPartitioning表示输入函数是否保留   分区器,应该是false,除非这是一对RDD和输入   功能不修改键。

     

列出变体

     

def mapPartitions [U:ClassTag](f:Iterator [T] =&gt; 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使用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) 

结论:

mapPartitions转换比map更快,因为它调用你的函数一次/分区,而不是一次/元素..

进一步阅读:foreach Vs foreachPartitions When to use What?

答案 2 :(得分:14)

地图

  
      
  1. 它一次处理一行,与MapReduce的map()方法非常相似。
  2.   
  3. 每行后都会从转换中返回。
  4.   

<强> MapPartitions

  
      
  1. 一次完成整个分区的处理。
  2.   
  3. 处理整个分区后,您只能从函数返回一次。
  4.   
  5. 所有中间结果都需要保存在内存中,直到您处理整个分区。
  6.   
  7. 为您提供MapReduce
  8. 的setup()map()和cleanup()函数         

    Map Vs mapPartitions   http://bytepadding.com/big-data/spark/spark-map-vs-mappartitions/

         

    Spark Map http://bytepadding.com/big-data/spark/spark-map/

         

    Spark mapPartitions   http://bytepadding.com/big-data/spark/spark-mappartitions/

答案 3 :(得分:0)

地图:

地图转换。

地图一次只处理一行。

Map 在每个输入行之后返回。

地图不将输出结果保存在内存中。

地图没办法弄清楚然后终止服务。

// map example

val dfList = (1 to 100) toList

val df = dfList.toDF()

val dfInt = df.map(x => x.getInt(0)+2)

display(dfInt)

地图分区:

MapPartition 转换。

MapPartition 一次作用于一个分区。

MapPartition 在处理完分区中的所有行后返回。

MapPartition 输出保留在内存中,因为它可以在处理特定分区中的所有行后返回。

MapPartition 服务可以在返回前关闭。

// MapPartition example

Val dfList = (1 to 100) toList

Val df = dfList.toDF()

Val df1 = df.repartition(4).rdd.mapPartition((int) => Iterator(itr.length))

Df1.collec()

//display(df1.collect())

有关详细信息,请参阅 Spark map vs mapPartitions transformation 文章。

希望对您有所帮助!