インプ。ヒント :
RDD
要素ごとに1回ではなく、多くの要素に対して1回実行する必要がある重い初期化があるRDD
場合、およびサードパーティライブラリからのオブジェクトの作成などの初期化をシリアル化できない場合(Sparkがクラスター全体でそれを送信できるようにするため)ワーカーノード)のmapPartitions()
代わりに
使用しますmap()
。たとえば、データ要素mapPartitions()
ごとに1回ではなく、ワーカータスク/スレッド/パーティションごとに1回実行される初期化を提供します。以下を参照してください。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
とmapPartitions
map
mapPartitions
パーティションレベルで機能を実行しながら、要素ごとのレベルで使用されて
いる機能を動作させます。
シナリオ例: 特定のRDD
パーティションに100Kの要素がある場合、マッピング変換によって使用されている関数を、使用時に100K回起動しますmap
。
逆に、使用する場合mapPartitions
、特定の関数を1回だけ呼び出しますが、すべての100Kレコードを渡して、1回の関数呼び出しですべての応答を返します。
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
これは、パーティションごとに1回だけ呼び出される特殊なマップです。各パーティションのコンテンツ全体は、入力引数(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
、関数を1回/要素ではなく1回/パーティションで呼び出すため、変換よりも高速です。
参考資料:foreachとforeachPartitionsの比較