兩者的主要區別是調用的粒度不一樣:map的輸入變換函數是應用於RDD中每個元素,而mapPartitions的輸入函數是應用於每個分區。
假設一個rdd有10個元素,分成3個分區。如果使用map方法,map中的輸入函數會被調用10次;而使用mapPartitions方法的話,其輸入函數會只會被調用3次,每個分區調用1次。
//生成10個元素3個分區的rdd a,元素值爲1~10的整數(1 2 3 4 5 6 7 8 9 10),sc爲SparkContext對象
val a = sc.parallelize(1 to 10, 3)
//定義兩個輸入變換函數,它們的作用均是將rdd a中的元素值翻倍
//map的輸入函數,其參數e爲rdd元素值
def myfuncPerElement(e:Int):Int = {
println("e="+e)
e*2
}
//mapPartitions的輸入函數。iter是分區中元素的迭代子,返回類型也要是迭代子
def myfuncPerPartition ( iter : Iterator [Int] ) : Iterator [Int] = {
println("run in partition")
var res = for (e <- iter ) yield e*2
res
}
val b = a.map(myfuncPerElement).collect
val c = a.mapPartitions(myfuncPerPartition).collect
在spark shell中運行上述代碼,可看到打印了3次run in partition,打印了10次e=。
從輸入函數(myfuncPerElement、myfuncPerPartition)層面來看,map是推模式,數據被推到myfuncPerElement中;mapPartitons是拉模式,myfuncPerPartition通過迭代子從分區中拉數據。
這兩個方法的另一個區別是在大數據集情況下的資源初始化開銷和批處理處理,如果在myfuncPerPartition和myfuncPerElement中都要初始化一個耗時的資源,然後使用,比如數據庫連接。在上面的例子中,myfuncPerPartition只需初始化3個資源(3個分區每個1次),而myfuncPerElement要初始化10次(10個元素每個1次),顯然在大數據集情況下(數據集中元素個數遠大於分區數),mapPartitons的開銷要小很多,也便於進行批處理操作。
mapPartitionsWithIndex和mapPartitons類似,只是其參數多了個分區索引號。
本文轉自:這裏